/* * Universal Interface for Intel High Definition Audio Codec * * Copyright (c) 2004 Takashi Iwai * * * This driver is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This driver is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include "hda_codec.h" #include #include #include #include #include "hda_local.h" #include "hda_beep.h" #include "hda_jack.h" #include #define CREATE_TRACE_POINTS #include "hda_trace.h" /* * vendor / preset table */ struct hda_vendor_id { unsigned int id; const char *name; }; /* codec vendor labels */ static struct hda_vendor_id hda_vendor_ids[] = { { 0x1002, "ATI" }, { 0x1013, "Cirrus Logic" }, { 0x1057, "Motorola" }, { 0x1095, "Silicon Image" }, { 0x10de, "Nvidia" }, { 0x10ec, "Realtek" }, { 0x1102, "Creative" }, { 0x1106, "VIA" }, { 0x111d, "IDT" }, { 0x11c1, "LSI" }, { 0x11d4, "Analog Devices" }, { 0x13f6, "C-Media" }, { 0x14f1, "Conexant" }, { 0x17e8, "Chrontel" }, { 0x1854, "LG" }, { 0x1aec, "Wolfson Microelectronics" }, { 0x434d, "C-Media" }, { 0x8086, "Intel" }, { 0x8384, "SigmaTel" }, {} /* terminator */ }; static DEFINE_MUTEX(preset_mutex); static LIST_HEAD(hda_preset_tables); int snd_hda_add_codec_preset(struct hda_codec_preset_list *preset) { mutex_lock(&preset_mutex); list_add_tail(&preset->list, &hda_preset_tables); mutex_unlock(&preset_mutex); return 0; } EXPORT_SYMBOL_HDA(snd_hda_add_codec_preset); int snd_hda_delete_codec_preset(struct hda_codec_preset_list *preset) { mutex_lock(&preset_mutex); list_del(&preset->list); mutex_unlock(&preset_mutex); return 0; } EXPORT_SYMBOL_HDA(snd_hda_delete_codec_preset); #ifdef CONFIG_SND_HDA_POWER_SAVE static void hda_power_work(struct work_struct *work); static void hda_keep_power_on(struct hda_codec *codec); #define hda_codec_is_power_on(codec) ((codec)->power_on) #else static inline void hda_keep_power_on(struct hda_codec *codec) {} #define hda_codec_is_power_on(codec) 1 #endif /** * snd_hda_get_jack_location - Give a location string of the jack * @cfg: pin default config value * * Parse the pin default config value and returns the string of the * jack location, e.g. "Rear", "Front", etc. */ const char *snd_hda_get_jack_location(u32 cfg) { static char *bases[7] = { "N/A", "Rear", "Front", "Left", "Right", "Top", "Bottom", }; static unsigned char specials_idx[] = { 0x07, 0x08, 0x17, 0x18, 0x19, 0x37, 0x38 }; static char *specials[] = { "Rear Panel", "Drive Bar", "Riser", "HDMI", "ATAPI", "Mobile-In", "Mobile-Out" }; int i; cfg = (cfg & AC_DEFCFG_LOCATION) >> AC_DEFCFG_LOCATION_SHIFT; if ((cfg & 0x0f) < 7) return bases[cfg & 0x0f]; for (i = 0; i < ARRAY_SIZE(specials_idx); i++) { if (cfg == specials_idx[i]) return specials[i]; } return "UNKNOWN"; } EXPORT_SYMBOL_HDA(snd_hda_get_jack_location); /** * snd_hda_get_jack_connectivity - Give a connectivity string of the jack * @cfg: pin default config value * * Parse the pin default config value and returns the string of the * jack connectivity, i.e. external or internal connection. */ const char *snd_hda_get_jack_connectivity(u32 cfg) { static char *jack_locations[4] = { "Ext", "Int", "Sep", "Oth" }; return jack_locations[(cfg >> (AC_DEFCFG_LOCATION_SHIFT + 4)) & 3]; } EXPORT_SYMBOL_HDA(snd_hda_get_jack_connectivity); /** * snd_hda_get_jack_type - Give a type string of the jack * @cfg: pin default config value * * Parse the pin default config value and returns the string of the * jack type, i.e. the purpose of the jack, such as Line-Out or CD. */ const char *snd_hda_get_jack_type(u32 cfg) { static char *jack_types[16] = { "Line Out", "Speaker", "HP Out", "CD", "SPDIF Out", "Digital Out", "Modem Line", "Modem Hand", "Line In", "Aux", "Mic", "Telephony", "SPDIF In", "Digitial In", "Reserved", "Other" }; return jack_types[(cfg & AC_DEFCFG_DEVICE) >> AC_DEFCFG_DEVICE_SHIFT]; } EXPORT_SYMBOL_HDA(snd_hda_get_jack_type); /* * Compose a 32bit command word to be sent to the HD-audio controller */ static inline unsigned int make_codec_cmd(struct hda_codec *codec, hda_nid_t nid, int direct, unsigned int verb, unsigned int parm) { u32 val; if ((codec->addr & ~0xf) || (direct & ~1) || (nid & ~0x7f) || (verb & ~0xfff) || (parm & ~0xffff)) { printk(KERN_ERR "hda-codec: out of range cmd %x:%x:%x:%x:%x\n", codec->addr, direct, nid, verb, parm); return ~0; } val = (u32)codec->addr << 28; val |= (u32)direct << 27; val |= (u32)nid << 20; val |= verb << 8; val |= parm; return val; } /* * Send and receive a verb */ static int codec_exec_verb(struct hda_codec *codec, unsigned int cmd, unsigned int *res) { struct hda_bus *bus = codec->bus; int err; if (cmd == ~0) return -1; if (res) *res = -1; again: snd_hda_power_up(codec); mutex_lock(&bus->cmd_mutex); trace_hda_send_cmd(codec, cmd); err = bus->ops.command(bus, cmd); if (!err && res) { *res = bus->ops.get_response(bus, codec->addr); trace_hda_get_response(codec, *res); } mutex_unlock(&bus->cmd_mutex); snd_hda_power_down(codec); if (res && *res == -1 && bus->rirb_error) { if (bus->response_reset) { snd_printd("hda_codec: resetting BUS due to " "fatal communication error\n"); trace_hda_bus_reset(bus); bus->ops.bus_reset(bus); } goto again; } /* clear reset-flag when the communication gets recovered */ if (!err) bus->response_reset = 0; return err; } /** * snd_hda_codec_read - send a command and get the response * @codec: the HDA codec * @nid: NID to send the command * @direct: direct flag * @verb: the verb to send * @parm: the parameter for the verb * * Send a single command and read the corresponding response. * * Returns the obtained response value, or -1 for an error. */ unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid, int direct, unsigned int verb, unsigned int parm) { unsigned cmd = make_codec_cmd(codec, nid, direct, verb, parm); unsigned int res; if (codec_exec_verb(codec, cmd, &res)) return -1; return res; } EXPORT_SYMBOL_HDA(snd_hda_codec_read); /** * snd_hda_codec_write - send a single command without waiting for response * @codec: the HDA codec * @nid: NID to send the command * @direct: direct flag * @verb: the verb to send * @parm: the parameter for the verb * * Send a single command without waiting for response. * * Returns 0 if successful, or a negative error code. */ int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int direct, unsigned int verb, unsigned int parm) { unsigned int cmd = make_codec_cmd(codec, nid, direct, verb, parm); unsigned int res; return codec_exec_verb(codec, cmd, codec->bus->sync_write ? &res : NULL); } EXPORT_SYMBOL_HDA(snd_hda_codec_write); /** * snd_hda_sequence_write - sequence writes * @codec: the HDA codec * @seq: VERB array to send * * Send the commands sequentially from the given array. * The array must be terminated with NID=0. */ void snd_hda_sequence_write(struct hda_codec *codec, const struct hda_verb *seq) { for (; seq->nid; seq++) snd_hda_codec_write(codec, seq->nid, 0, seq->verb, seq->param); } EXPORT_SYMBOL_HDA(snd_hda_sequence_write); /** * snd_hda_get_sub_nodes - get the range of sub nodes * @codec: the HDA codec * @nid: NID to parse * @start_id: the pointer to store the start NID * * Parse the NID and store the start NID of its sub-nodes. * Returns the number of sub-nodes. */ int snd_hda_get_sub_nodes(struct hda_codec *codec, hda_nid_t nid, hda_nid_t *start_id) { unsigned int parm; parm = snd_hda_param_read(codec, nid, AC_PAR_NODE_COUNT); if (parm == -1) return 0; *start_id = (parm >> 16) & 0x7fff; return (int)(parm & 0x7fff); } EXPORT_SYMBOL_HDA(snd_hda_get_sub_nodes); /* look up the cached results */ static hda_nid_t *lookup_conn_list(struct snd_array *array, hda_nid_t nid) { int i, len; for (i = 0; i < array->used; ) { hda_nid_t *p = snd_array_elem(array, i); if (nid == *p) return p; len = p[1]; i += len + 2; } return NULL; } /* read the connection and add to the cache */ static int read_and_add_raw_conns(struct hda_codec *codec, hda_nid_t nid) { hda_nid_t list[HDA_MAX_CONNECTIONS]; int len; len = snd_hda_get_raw_connections(codec, nid, list, ARRAY_SIZE(list)); if (len < 0) return len; return snd_hda_override_conn_list(codec, nid, len, list); } /** * snd_hda_get_connections - copy connection list * @codec: the HDA codec * @nid: NID to parse * @conn_list: connection list array; when NULL, checks only the size * @max_conns: max. number of connections to store * * Parses the connection list of the given widget and stores the list * of NIDs. * * Returns the number of connections, or a negative error code. */ int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid, hda_nid_t *conn_list, int max_conns) { struct snd_array *array = &codec->conn_lists; int len; hda_nid_t *p; bool added = false; again: mutex_lock(&codec->hash_mutex); len = -1; /* if the connection-list is already cached, read it */ p = lookup_conn_list(array, nid); if (p) { len = p[1]; if (conn_list && len > max_conns) { snd_printk(KERN_ERR "hda_codec: " "Too many connections %d for NID 0x%x\n", len, nid); mutex_unlock(&codec->hash_mutex); return -EINVAL; } if (conn_list && len) memcpy(conn_list, p + 2, len * sizeof(hda_nid_t)); } mutex_unlock(&codec->hash_mutex); if (len >= 0) return len; if (snd_BUG_ON(added)) return -EINVAL; len = read_and_add_raw_conns(codec, nid); if (len < 0) return len; added = true; goto again; } EXPORT_SYMBOL_HDA(snd_hda_get_connections); /** * snd_hda_get_raw_connections - copy connection list without cache * @codec: the HDA codec * @nid: NID to parse * @conn_list: connection list array * @max_conns: max. number of connections to store * * Like snd_hda_get_connections(), copy the connection list but without * checking through the connection-list cache. * Currently called only from hda_proc.c, so not exported. */ int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid, hda_nid_t *conn_list, int max_conns) { unsigned int parm; int i, conn_len, conns; unsigned int shift, num_elems, mask; unsigned int wcaps; hda_nid_t prev_nid; if (snd_BUG_ON(!conn_list || max_conns <= 0)) return -EINVAL; wcaps = get_wcaps(codec, nid); if (!(wcaps & AC_WCAP_CONN_LIST) && get_wcaps_type(wcaps) != AC_WID_VOL_KNB) return 0; parm = snd_hda_param_read(codec, nid, AC_PAR_CONNLIST_LEN); if (parm & AC_CLIST_LONG) { /* long form */ shift = 16; num_elems = 2; } else { /* short form */ shift = 8; num_elems = 4; } conn_len = parm & AC_CLIST_LENGTH; mask = (1 << (shift-1)) - 1; if (!conn_len) return 0; /* no connection */ if (conn_len == 1) { /* single connection */ parm = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONNECT_LIST, 0); if (parm == -1 && codec->bus->rirb_error) return -EIO; conn_list[0] = parm & mask; return 1; } /* multi connection */ conns = 0; prev_nid = 0; for (i = 0; i < conn_len; i++) { int range_val; hda_nid_t val, n; if (i % num_elems == 0) { parm = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONNECT_LIST, i); if (parm == -1 && codec->bus->rirb_error) return -EIO; } range_val = !!(parm & (1 << (shift-1))); /* ranges */ val = parm & mask; if (val == 0) { snd_printk(KERN_WARNING "hda_codec: " "invalid CONNECT_LIST verb %x[%i]:%x\n", nid, i, parm); return 0; } parm >>= shift; if (range_val) { /* ranges between the previous and this one */ if (!prev_nid || prev_nid >= val) { snd_printk(KERN_WARNING "hda_codec: " "invalid dep_range_val %x:%x\n", prev_nid, val); continue; } for (n = prev_nid + 1; n <= val; n++) { if (conns >= max_conns) { snd_printk(KERN_ERR "hda_codec: " "Too many connections %d for NID 0x%x\n", conns, nid); return -EINVAL; } conn_list[conns++] = n; } } else { if (conns >= max_conns) { snd_printk(KERN_ERR "hda_codec: " "Too many connections %d for NID 0x%x\n", conns, nid); return -EINVAL; } conn_list[conns++] = val; } prev_nid = val; } return conns; } static bool add_conn_list(struct snd_array *array, hda_nid_t nid) { hda_nid_t *p = snd_array_new(array); if (!p) return false; *p = nid; return true; } /** * snd_hda_override_conn_list - add/modify the connection-list to cache * @codec: the HDA codec * @nid: NID to parse * @len: number of connection list entries * @list: the list of connection entries * * Add or modify the given connection-list to the cache. If the corresponding * cache already exists, invalidate it and append a new one. * * Returns zero or a negative error code. */ int snd_hda_override_conn_list(struct hda_codec *codec, hda_nid_t nid, int len, const hda_nid_t *list) { struct snd_array *array = &codec->conn_lists; hda_nid_t *p; int i, old_used; mutex_lock(&codec->hash_mutex); p = lookup_conn_list(array, nid); if (p) *p = -1; /* invalidate the old entry */ old_used = array->used; if (!add_conn_list(array, nid) || !add_conn_list(array, len)) goto error_add; for (i = 0; i < len; i++) if (!add_conn_list(array, list[i])) goto error_add; mutex_unlock(&codec->hash_mutex); return 0; error_add: array->used = old_used; mutex_unlock(&codec->hash_mutex); return -ENOMEM; } EXPORT_SYMBOL_HDA(snd_hda_override_conn_list); /** * snd_hda_get_conn_index - get the connection index of the given NID * @codec: the HDA codec * @mux: NID containing the list * @nid: NID to select * @recursive: 1 when searching NID recursively, otherwise 0 * * Parses the connection list of the widget @mux and checks whether the * widget @nid is present. If it is, return the connection index. * Otherwise it returns -1. */ int snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux, hda_nid_t nid, int recursive) { hda_nid_t conn[HDA_MAX_NUM_INPUTS]; int i, nums; nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn)); for (i = 0; i < nums; i++) if (conn[i] == nid) return i; if (!recursive) return -1; if (recursive > 5) { snd_printd("hda_codec: too deep connection for 0x%x\n", nid); return -1; } recursive++; for (i = 0; i < nums; i++) { unsigned int type = get_wcaps_type(get_wcaps(codec, conn[i])); if (type == AC_WID_PIN || type == AC_WID_AUD_OUT) continue; if (snd_hda_get_conn_index(codec, conn[i], nid, recursive) >= 0) return i; } return -1; } EXPORT_SYMBOL_HDA(snd_hda_get_conn_index); /** * snd_hda_queue_unsol_event - add an unsolicited event to queue * @bus: the BUS * @res: unsolicited event (lower 32bit of RIRB entry) * @res_ex: codec addr and flags (upper 32bit or RIRB entry) * * Adds the given event to the queue. The events are processed in * the workqueue asynchronously. Call this function in the interrupt * hanlder when RIRB receives an unsolicited event. * * Returns 0 if successful, or a negative error code. */ int snd_hda_queue_unsol_event(struct hda_bus *bus, u32 res, u32 res_ex) { struct hda_bus_unsolicited *unsol; unsigned int wp; trace_hda_unsol_event(bus, res, res_ex); unsol = bus->unsol; if (!unsol) return 0; wp = (unsol->wp + 1) % HDA_UNSOL_QUEUE_SIZE; unsol->wp = wp; wp <<= 1; unsol->queue[wp] = res; unsol->queue[wp + 1] = res_ex; queue_work(bus->workq, &unsol->work); return 0; } EXPORT_SYMBOL_HDA(snd_hda_queue_unsol_event); /* * process queued unsolicited events */ static void process_unsol_events(struct work_struct *work) { struct hda_bus_unsolicited *unsol = container_of(work, struct hda_bus_unsolicited, work); struct hda_bus *bus = unsol->bus; struct hda_codec *codec; unsigned int rp, caddr, res; while (unsol->rp != unsol->wp) { rp = (unsol->rp + 1) % HDA_UNSOL_QUEUE_SIZE; unsol->rp = rp; rp <<= 1; res = unsol->queue[rp]; caddr = unsol->queue[rp + 1]; if (!(caddr & (1 << 4))) /* no unsolicited event? */ continue; codec = bus->caddr_tbl[caddr & 0x0f]; if (codec && codec->patch_ops.unsol_event) codec->patch_ops.unsol_event(codec, res); } } /* * initialize unsolicited queue */ static int init_unsol_queue(struct hda_bus *bus) { struct hda_bus_unsolicited *unsol; if (bus->unsol) /* already initialized */ return 0; unsol = kzalloc(sizeof(*unsol), GFP_KERNEL); if (!unsol) { snd_printk(KERN_ERR "hda_codec: " "can't allocate unsolicited queue\n"); return -ENOMEM; } INIT_WORK(&unsol->work, process_unsol_events); unsol->bus = bus; bus->unsol = unsol; return 0; } /* * destructor */ static void snd_hda_codec_free(struct hda_codec *codec); static int snd_hda_bus_free(struct hda_bus *bus) { struct hda_codec *codec, *n; if (!bus) return 0; if (bus->workq) flush_workqueue(bus->workq); if (bus->unsol) kfree(bus->unsol); list_for_each_entry_safe(codec, n, &bus->codec_list, list) { snd_hda_codec_free(codec); } if (bus->ops.private_free) bus->ops.private_free(bus); if (bus->workq) destroy_workqueue(bus->workq); kfree(bus); return 0; } static int snd_hda_bus_dev_free(struct snd_device *device) { struct hda_bus *bus = device->device_data; bus->shutdown = 1; return snd_hda_bus_free(bus); } #ifdef CONFIG_SND_HDA_HWDEP static int snd_hda_bus_dev_register(struct snd_device *device) { struct hda_bus *bus = device->device_data; struct hda_codec *codec; list_for_each_entry(codec, &bus->codec_list, list) { snd_hda_hwdep_add_sysfs(codec); snd_hda_hwdep_add_power_sysfs(codec); } return 0; } #else #define snd_hda_bus_dev_register NULL #endif /** * snd_hda_bus_new - create a HDA bus * @card: the card entry * @temp: the template for hda_bus information * @busp: the pointer to store the created bus instance * * Returns 0 if successful, or a negative error code. */ int /*__devinit*/ snd_hda_bus_new(struct snd_card *card, const struct hda_bus_template *temp, struct hda_bus **busp) { struct hda_bus *bus; int err; static struct snd_device_ops dev_ops = { .dev_register = snd_hda_bus_dev_register, .dev_free = snd_hda_bus_dev_free, }; if (snd_BUG_ON(!temp)) return -EINVAL; if (snd_BUG_ON(!temp->ops.command || !temp->ops.get_response)) return -EINVAL; if (busp) *busp = NULL; bus = kzalloc(sizeof(*bus), GFP_KERNEL); if (bus == NULL) { snd_printk(KERN_ERR "can't allocate struct hda_bus\n"); return -ENOMEM; } bus->card = card; bus->private_data = temp->private_data; bus->pci = temp->pci; bus->modelname = temp->modelname; bus->power_save = temp->power_save; bus->ops = temp->ops; mutex_init(&bus->cmd_mutex); mutex_init(&bus->prepare_mutex); INIT_LIST_HEAD(&bus->codec_list); snprintf(bus->workq_name, sizeof(bus->workq_name), "hd-audio%d", card->number); bus->workq = create_singlethread_workqueue(bus->workq_name); if (!bus->workq) { snd_printk(KERN_ERR "cannot create workqueue %s\n", bus->workq_name); kfree(bus); return -ENOMEM; } err = snd_device_new(card, SNDRV_DEV_BUS, bus, &dev_ops); if (err < 0) { snd_hda_bus_free(bus); return err; } if (busp) *busp = bus; return 0; } EXPORT_SYMBOL_HDA(snd_hda_bus_new); #ifdef CONFIG_SND_HDA_GENERIC #define is_generic_config(codec) \ (codec->modelname && !strcmp(codec->modelname, "generic")) #else #define is_generic_config(codec) 0 #endif #ifdef MODULE #define HDA_MODREQ_MAX_COUNT 2 /* two request_modules()'s */ #else #define HDA_MODREQ_MAX_COUNT 0 /* all presets are statically linked */ #endif /* * find a matching codec preset */ static const struct hda_codec_preset * find_codec_preset(struct hda_codec *codec) { struct hda_codec_preset_list *tbl; const struct hda_codec_preset *preset; int mod_requested = 0; if (is_generic_config(codec)) return NULL; /* use the generic parser */ again: mutex_lock(&preset_mutex); list_for_each_entry(tbl, &hda_preset_tables, list) { if (!try_module_get(tbl->owner)) { snd_printk(KERN_ERR "hda_codec: cannot module_get\n"); continue; } for (preset = tbl->preset; preset->id; preset++) { u32 mask = preset->mask; if (preset->afg && preset->afg != codec->afg) continue; if (preset->mfg && preset->mfg != codec->mfg) continue; if (!mask) mask = ~0; if (preset->id == (codec->vendor_id & mask) && (!preset->rev || preset->rev == codec->revision_id)) { mutex_unlock(&preset_mutex); codec->owner = tbl->owner; return preset; } } module_put(tbl->owner); } mutex_unlock(&preset_mutex); if (mod_requested < HDA_MODREQ_MAX_COUNT) { char name[32]; if (!mod_requested) snprintf(name, sizeof(name), "snd-hda-codec-id:%08x", codec->vendor_id); else snprintf(name, sizeof(name), "snd-hda-codec-id:%04x*", (codec->vendor_id >> 16) & 0xffff); request_module(name); mod_requested++; goto again; } return NULL; } /* * get_codec_name - store the codec name */ static int get_codec_name(struct hda_codec *codec) { const struct hda_vendor_id *c; const char *vendor = NULL; u16 vendor_id = codec->vendor_id >> 16; char tmp[16]; if (codec->vendor_name) goto get_chip_name; for (c = hda_vendor_ids; c->id; c++) { if (c->id == vendor_id) { vendor = c->name; break; } } if (!vendor) { sprintf(tmp, "Generic %04x", vendor_id); vendor = tmp; } codec->vendor_name = kstrdup(vendor, GFP_KERNEL); if (!codec->vendor_name) return -ENOMEM; get_chip_name: if (codec->chip_name) return 0; if (codec->preset && codec->preset->name) codec->chip_name = kstrdup(codec->preset->name, GFP_KERNEL); else { sprintf(tmp, "ID %x", codec->vendor_id & 0xffff); codec->chip_name = kstrdup(tmp, GFP_KERNEL); } if (!codec->chip_name) return -ENOMEM; return 0; } /* * look for an AFG and MFG nodes */ static void /*__devinit*/ setup_fg_nodes(struct hda_codec *codec) { int i, total_nodes, function_id; hda_nid_t nid; total_nodes = snd_hda_get_sub_nodes(codec, AC_NODE_ROOT, &nid); for (i = 0; i < total_nodes; i++, nid++) { function_id = snd_hda_param_read(codec, nid, AC_PAR_FUNCTION_TYPE); switch (function_id & 0xff) { case AC_GRP_AUDIO_FUNCTION: codec->afg = nid; codec->afg_function_id = function_id & 0xff; codec->afg_unsol = (function_id >> 8) & 1; break; case AC_GRP_MODEM_FUNCTION: codec->mfg = nid; codec->mfg_function_id = function_id & 0xff; codec->mfg_unsol = (function_id >> 8) & 1; break; default: break; } } } /* * read widget caps for each widget and store in cache */ static int read_widget_caps(struct hda_codec *codec, hda_nid_t fg_node) { int i; hda_nid_t nid; codec->num_nodes = snd_hda_get_sub_nodes(codec, fg_node, &codec->start_nid); codec->wcaps = kmalloc(codec->num_nodes * 4, GFP_KERNEL); if (!codec->wcaps) return -ENOMEM; nid = codec->start_nid; for (i = 0; i < codec->num_nodes; i++, nid++) codec->wcaps[i] = snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP); return 0; } /* read all pin default configurations and save codec->init_pins */ static int read_pin_defaults(struct hda_codec *codec) { int i; hda_nid_t nid = codec->start_nid; for (i = 0; i < codec->num_nodes; i++, nid++) { struct hda_pincfg *pin; unsigned int wcaps = get_wcaps(codec, nid); unsigned int wid_type = get_wcaps_type(wcaps); if (wid_type != AC_WID_PIN) continue; pin = snd_array_new(&codec->init_pins); if (!pin) return -ENOMEM; pin->nid = nid; pin->cfg = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONFIG_DEFAULT, 0); pin->ctrl = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0); } return 0; } /* look up the given pin config list and return the item matching with NID */ static struct hda_pincfg *look_up_pincfg(struct hda_codec *codec, struct snd_array *array, hda_nid_t nid) { int i; for (i = 0; i < array->used; i++) { struct hda_pincfg *pin = snd_array_elem(array, i); if (pin->nid == nid) return pin; } return NULL; } /* write a config value for the given NID */ static void set_pincfg(struct hda_codec *codec, hda_nid_t nid, unsigned int cfg) { int i; for (i = 0; i < 4; i++) { snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONFIG_DEFAULT_BYTES_0 + i, cfg & 0xff); cfg >>= 8; } } /* set the current pin config value for the given NID. * the value is cached, and read via snd_hda_codec_get_pincfg() */ int snd_hda_add_pincfg(struct hda_codec *codec, struct snd_array *list, hda_nid_t nid, unsigned int cfg) { struct hda_pincfg *pin; unsigned int oldcfg; if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN) return -EINVAL; oldcfg = snd_hda_codec_get_pincfg(codec, nid); pin = look_up_pincfg(codec, list, nid); if (!pin) { pin = snd_array_new(list); if (!pin) return -ENOMEM; pin->nid = nid; } pin->cfg = cfg; /* change only when needed; e.g. if the pincfg is already present * in user_pins[], don't write it */ cfg = snd_hda_codec_get_pincfg(codec, nid); if (oldcfg != cfg) set_pincfg(codec, nid, cfg); return 0; } /** * snd_hda_codec_set_pincfg - Override a pin default configuration * @codec: the HDA codec * @nid: NID to set the pin config * @cfg: the pin default config value * * Override a pin default configuration value in the cache. * This value can be read by snd_hda_codec_get_pincfg() in a higher * priority than the real hardware value. */ int snd_hda_codec_set_pincfg(struct hda_codec *codec, hda_nid_t nid, unsigned int cfg) { return snd_hda_add_pincfg(codec, &codec->driver_pins, nid, cfg); } EXPORT_SYMBOL_HDA(snd_hda_codec_set_pincfg); /** * snd_hda_codec_get_pincfg - Obtain a pin-default configuration * @codec: the HDA codec * @nid: NID to get the pin config * * Get the current pin config value of the given pin NID. * If the pincfg value is cached or overridden via sysfs or driver, * returns the cached value. */ unsigned int snd_hda_codec_get_pincfg(struct hda_codec *codec, hda_nid_t nid) { struct hda_pincfg *pin; #ifdef CONFIG_SND_HDA_HWDEP pin = look_up_pincfg(codec, &codec->user_pins, nid); if (pin) return pin->cfg; #endif pin = look_up_pincfg(codec, &codec->driver_pins, nid); if (pin) return pin->cfg; pin = look_up_pincfg(codec, &codec->init_pins, nid); if (pin) return pin->cfg; return 0; } EXPORT_SYMBOL_HDA(snd_hda_codec_get_pincfg); /* restore all current pin configs */ static void restore_pincfgs(struct hda_codec *codec) { int i; for (i = 0; i < codec->init_pins.used; i++) { struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i); set_pincfg(codec, pin->nid, snd_hda_codec_get_pincfg(codec, pin->nid)); } } /** * snd_hda_shutup_pins - Shut up all pins * @codec: the HDA codec * * Clear all pin controls to shup up before suspend for avoiding click noise. * The controls aren't cached so that they can be resumed properly. */ void snd_hda_shutup_pins(struct hda_codec *codec) { int i; /* don't shut up pins when unloading the driver; otherwise it breaks * the default pin setup at the next load of the driver */ if (codec->bus->shutdown) return; for (i = 0; i < codec->init_pins.used; i++) { struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i); /* use read here for syncing after issuing each verb */ snd_hda_codec_read(codec, pin->nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0); } codec->pins_shutup = 1; } EXPORT_SYMBOL_HDA(snd_hda_shutup_pins); #ifdef CONFIG_PM /* Restore the pin controls cleared previously via snd_hda_shutup_pins() */ static void restore_shutup_pins(struct hda_codec *codec) { int i; if (!codec->pins_shutup) return; if (codec->bus->shutdown) return; for (i = 0; i < codec->init_pins.used; i++) { struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i); snd_hda_codec_write(codec, pin->nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin->ctrl); } codec->pins_shutup = 0; } #endif static void init_hda_cache(struct hda_cache_rec *cache, unsigned int record_size); static void free_hda_cache(struct hda_cache_rec *cache); /* restore the initial pin cfgs and release all pincfg lists */ static void restore_init_pincfgs(struct hda_codec *codec) { /* first free driver_pins and user_pins, then call restore_pincfg * so that only the values in init_pins are restored */ snd_array_free(&codec->driver_pins); #ifdef CONFIG_SND_HDA_HWDEP snd_array_free(&codec->user_pins); #endif restore_pincfgs(codec); snd_array_free(&codec->init_pins); } /* * audio-converter setup caches */ struct hda_cvt_setup { hda_nid_t nid; u8 stream_tag; u8 channel_id; u16 format_id; unsigned char active; /* cvt is currently used */ unsigned char dirty; /* setups should be cleared */ }; /* get or create a cache entry for the given audio converter NID */ static struct hda_cvt_setup * get_hda_cvt_setup(struct hda_codec *codec, hda_nid_t nid) { struct hda_cvt_setup *p; int i; for (i = 0; i < codec->cvt_setups.used; i++) { p = snd_array_elem(&codec->cvt_setups, i); if (p->nid == nid) return p; } p = snd_array_new(&codec->cvt_setups); if (p) p->nid = nid; return p; } /* * codec destructor */ static void snd_hda_codec_free(struct hda_codec *codec) { if (!codec) return; restore_init_pincfgs(codec); #ifdef CONFIG_SND_HDA_POWER_SAVE cancel_delayed_work(&codec->power_work); flush_workqueue(codec->bus->workq); #endif list_del(&codec->list); snd_array_free(&codec->mixers); snd_array_free(&codec->nids); snd_array_free(&codec->conn_lists); snd_array_free(&codec->spdif_out); codec->bus->caddr_tbl[codec->addr] = NULL; if (codec->patch_ops.free) codec->patch_ops.free(codec); module_put(codec->owner); free_hda_cache(&codec->amp_cache); free_hda_cache(&codec->cmd_cache); kfree(codec->vendor_name); kfree(codec->chip_name); kfree(codec->modelname); kfree(codec->wcaps); kfree(codec); } static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg, unsigned int power_state); /** * snd_hda_codec_new - create a HDA codec * @bus: the bus to assign * @codec_addr: the codec address * @codecp: the pointer to store the generated codec * * Returns 0 if successful, or a negative error code. */ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr, struct hda_codec **codecp) { struct hda_codec *codec; char component[31]; int err; if (snd_BUG_ON(!bus)) return -EINVAL; if (snd_BUG_ON(codec_addr > HDA_MAX_CODEC_ADDRESS)) return -EINVAL; if (bus->caddr_tbl[codec_addr]) { snd_printk(KERN_ERR "hda_codec: " "address 0x%x is already occupied\n", codec_addr); return -EBUSY; } codec = kzalloc(sizeof(*codec), GFP_KERNEL); if (codec == NULL) { snd_printk(KERN_ERR "can't allocate struct hda_codec\n"); return -ENOMEM; } codec->bus = bus; codec->addr = codec_addr; mutex_init(&codec->spdif_mutex); mutex_init(&codec->control_mutex); mutex_init(&codec->hash_mutex); init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info)); init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head)); snd_array_init(&codec->mixers, sizeof(struct hda_nid_item), 32); snd_array_init(&codec->nids, sizeof(struct hda_nid_item), 32); snd_array_init(&codec->init_pins, sizeof(struct hda_pincfg), 16); snd_array_init(&codec->driver_pins, sizeof(struct hda_pincfg), 16); snd_array_init(&codec->cvt_setups, sizeof(struct hda_cvt_setup), 8); snd_array_init(&codec->conn_lists, sizeof(hda_nid_t), 64); snd_array_init(&codec->spdif_out, sizeof(struct hda_spdif_out), 16); #ifdef CONFIG_SND_HDA_POWER_SAVE spin_lock_init(&codec->power_lock); INIT_DELAYED_WORK(&codec->power_work, hda_power_work); /* snd_hda_codec_new() marks the codec as power-up, and leave it as is. * the caller has to power down appropriatley after initialization * phase. */ hda_keep_power_on(codec); #endif if (codec->bus->modelname) { codec->modelname = kstrdup(codec->bus->modelname, GFP_KERNEL); if (!codec->modelname) { snd_hda_codec_free(codec); return -ENODEV; } } list_add_tail(&codec->list, &bus->codec_list); bus->caddr_tbl[codec_addr] = codec; codec->vendor_id = snd_hda_param_read(codec, AC_NODE_ROOT, AC_PAR_VENDOR_ID); if (codec->vendor_id == -1) /* read again, hopefully the access method was corrected * in the last read... */ codec->vendor_id = snd_hda_param_read(codec, AC_NODE_ROOT, AC_PAR_VENDOR_ID); codec->subsystem_id = snd_hda_param_read(codec, AC_NODE_ROOT, AC_PAR_SUBSYSTEM_ID); codec->revision_id = snd_hda_param_read(codec, AC_NODE_ROOT, AC_PAR_REV_ID); setup_fg_nodes(codec); if (!codec->afg && !codec->mfg) { snd_printdd("hda_codec: no AFG or MFG node found\n"); err = -ENODEV; goto error; } err = read_widget_caps(codec, codec->afg ? codec->afg : codec->mfg); if (err < 0) { snd_printk(KERN_ERR "hda_codec: cannot malloc\n"); goto error; } err = read_pin_defaults(codec); if (err < 0) goto error; if (!codec->subsystem_id) { hda_nid_t nid = codec->afg ? codec->afg : codec->mfg; codec->subsystem_id = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_SUBSYSTEM_ID, 0); } /* power-up all before initialization */ hda_set_power_state(codec, codec->afg ? codec->afg : codec->mfg, AC_PWRST_D0); snd_hda_codec_proc_new(codec); snd_hda_create_hwdep(codec); sprintf(component, "HDA:%08x,%08x,%08x", codec->vendor_id, codec->subsystem_id, codec->revision_id); snd_component_add(codec->bus->card, component); if (codecp) *codecp = codec; return 0; error: snd_hda_codec_free(codec); return err; } EXPORT_SYMBOL_HDA(snd_hda_codec_new); /** * snd_hda_codec_configure - (Re-)configure the HD-audio codec * @codec: the HDA codec * * Start parsing of the given codec tree and (re-)initialize the whole * patch instance. * * Returns 0 if successful or a negative error code. */ int snd_hda_codec_configure(struct hda_codec *codec) { int err; codec->preset = find_codec_preset(codec); if (!codec->vendor_name || !codec->chip_name) { err = get_codec_name(codec); if (err < 0) return err; } if (is_generic_config(codec)) { err = snd_hda_parse_generic_codec(codec); goto patched; } if (codec->preset && codec->preset->patch) { err = codec->preset->patch(codec); goto patched; } /* call the default parser */ err = snd_hda_parse_generic_codec(codec); if (err < 0) printk(KERN_ERR "hda-codec: No codec parser is available\n"); patched: if (!err && codec->patch_ops.unsol_event) err = init_unsol_queue(codec->bus); /* audio codec should override the mixer name */ if (!err && (codec->afg || !*codec->bus->card->mixername)) snprintf(codec->bus->card->mixername, sizeof(codec->bus->card->mixername), "%s %s", codec->vendor_name, codec->chip_name); return err; } EXPORT_SYMBOL_HDA(snd_hda_codec_configure); /** * snd_hda_codec_setup_stream - set up the codec for streaming * @codec: the CODEC to set up * @nid: the NID to set up * @stream_tag: stream tag to pass, it's between 0x1 and 0xf. * @channel_id: channel id to pass, zero based. * @format: stream format. */ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid, u32 stream_tag, int channel_id, int format) { struct hda_codec *c; struct hda_cvt_setup *p; unsigned int oldval, newval; int type; int i; if (!nid) return; snd_printdd("hda_codec_setup_stream: " "NID=0x%x, stream=0x%x, channel=%d, format=0x%x\n", nid, stream_tag, channel_id, format); p = get_hda_cvt_setup(codec, nid); if (!p) return; /* update the stream-id if changed */ if (p->stream_tag != stream_tag || p->channel_id != channel_id) { oldval = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0); newval = (stream_tag << 4) | channel_id; if (oldval != newval) snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CHANNEL_STREAMID, newval); p->stream_tag = stream_tag; p->channel_id = channel_id; } /* update the format-id if changed */ if (p->format_id != format) { oldval = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_STREAM_FORMAT, 0); if (oldval != format) { msleep(1); snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, format); } p->format_id = format; } p->active = 1; p->dirty = 0; /* make other inactive cvts with the same stream-tag dirty */ type = get_wcaps_type(get_wcaps(codec, nid)); list_for_each_entry(c, &codec->bus->codec_list, list) { for (i = 0; i < c->cvt_setups.used; i++) { p = snd_array_elem(&c->cvt_setups, i); if (!p->active && p->stream_tag == stream_tag && get_wcaps_type(get_wcaps(c, p->nid)) == type) p->dirty = 1; } } } EXPORT_SYMBOL_HDA(snd_hda_codec_setup_stream); static void really_cleanup_stream(struct hda_codec *codec, struct hda_cvt_setup *q); /** * __snd_hda_codec_cleanup_stream - clean up the codec for closing * @codec: the CODEC to clean up * @nid: the NID to clean up * @do_now: really clean up the stream instead of clearing the active flag */ void __snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid, int do_now) { struct hda_cvt_setup *p; if (!nid) return; if (codec->no_sticky_stream) do_now = 1; snd_printdd("hda_codec_cleanup_stream: NID=0x%x\n", nid); p = get_hda_cvt_setup(codec, nid); if (p) { /* here we just clear the active flag when do_now isn't set; * actual clean-ups will be done later in * purify_inactive_streams() called from snd_hda_codec_prpapre() */ if (do_now) really_cleanup_stream(codec, p); else p->active = 0; } } EXPORT_SYMBOL_HDA(__snd_hda_codec_cleanup_stream); static void really_cleanup_stream(struct hda_codec *codec, struct hda_cvt_setup *q) { hda_nid_t nid = q->nid; if (q->stream_tag || q->channel_id) snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0); if (q->format_id) snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, 0 ); memset(q, 0, sizeof(*q)); q->nid = nid; } /* clean up the all conflicting obsolete streams */ static void purify_inactive_streams(struct hda_codec *codec) { struct hda_codec *c; int i; list_for_each_entry(c, &codec->bus->codec_list, list) { for (i = 0; i < c->cvt_setups.used; i++) { struct hda_cvt_setup *p; p = snd_array_elem(&c->cvt_setups, i); if (p->dirty) really_cleanup_stream(c, p); } } } #ifdef CONFIG_PM /* clean up all streams; called from suspend */ static void hda_cleanup_all_streams(struct hda_codec *codec) { int i; for (i = 0; i < codec->cvt_setups.used; i++) { struct hda_cvt_setup *p = snd_array_elem(&codec->cvt_setups, i); if (p->stream_tag) really_cleanup_stream(codec, p); } } #endif /* * amp access functions */ /* FIXME: more better hash key? */ #define HDA_HASH_KEY(nid, dir, idx) (u32)((nid) + ((idx) << 16) + ((dir) << 24)) #define HDA_HASH_PINCAP_KEY(nid) (u32)((nid) + (0x02 << 24)) #define HDA_HASH_PARPCM_KEY(nid) (u32)((nid) + (0x03 << 24)) #define HDA_HASH_PARSTR_KEY(nid) (u32)((nid) + (0x04 << 24)) #define INFO_AMP_CAPS (1<<0) #define INFO_AMP_VOL(ch) (1 << (1 + (ch))) /* initialize the hash table */ static void /*__devinit*/ init_hda_cache(struct hda_cache_rec *cache, unsigned int record_size) { memset(cache, 0, sizeof(*cache)); memset(cache->hash, 0xff, sizeof(cache->hash)); snd_array_init(&cache->buf, record_size, 64); } static void free_hda_cache(struct hda_cache_rec *cache) { snd_array_free(&cache->buf); } /* query the hash. allocate an entry if not found. */ static struct hda_cache_head *get_hash(struct hda_cache_rec *cache, u32 key) { u16 idx = key % (u16)ARRAY_SIZE(cache->hash); u16 cur = cache->hash[idx]; struct hda_cache_head *info; while (cur != 0xffff) { info = snd_array_elem(&cache->buf, cur); if (info->key == key) return info; cur = info->next; } return NULL; } /* query the hash. allocate an entry if not found. */ static struct hda_cache_head *get_alloc_hash(struct hda_cache_rec *cache, u32 key) { struct hda_cache_head *info = get_hash(cache, key); if (!info) { u16 idx, cur; /* add a new hash entry */ info = snd_array_new(&cache->buf); if (!info) return NULL; cur = snd_array_index(&cache->buf, info); info->key = key; info->val = 0; idx = key % (u16)ARRAY_SIZE(cache->hash); info->next = cache->hash[idx]; cache->hash[idx] = cur; } return info; } /* query and allocate an amp hash entry */ static inline struct hda_amp_info * get_alloc_amp_hash(struct hda_codec *codec, u32 key) { return (struct hda_amp_info *)get_alloc_hash(&codec->amp_cache, key); } /* overwrite the value with the key in the caps hash */ static int write_caps_hash(struct hda_codec *codec, u32 key, unsigned int val) { struct hda_amp_info *info; mutex_lock(&codec->hash_mutex); info = get_alloc_amp_hash(codec, key); if (!info) { mutex_unlock(&codec->hash_mutex); return -EINVAL; } info->amp_caps = val; info->head.val |= INFO_AMP_CAPS; mutex_unlock(&codec->hash_mutex); return 0; } /* query the value from the caps hash; if not found, fetch the current * value from the given function and store in the hash */ static unsigned int query_caps_hash(struct hda_codec *codec, hda_nid_t nid, int dir, u32 key, unsigned int (*func)(struct hda_codec *, hda_nid_t, int)) { struct hda_amp_info *info; unsigned int val; mutex_lock(&codec->hash_mutex); info = get_alloc_amp_hash(codec, key); if (!info) { mutex_unlock(&codec->hash_mutex); return 0; } if (!(info->head.val & INFO_AMP_CAPS)) { mutex_unlock(&codec->hash_mutex); /* for reentrance */ val = func(codec, nid, dir); write_caps_hash(codec, key, val); } else { val = info->amp_caps; mutex_unlock(&codec->hash_mutex); } return val; } static unsigned int read_amp_cap(struct hda_codec *codec, hda_nid_t nid, int direction) { if (!(get_wcaps(codec, nid) & AC_WCAP_AMP_OVRD)) nid = codec->afg; return snd_hda_param_read(codec, nid, direction == HDA_OUTPUT ? AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP); } /** * query_amp_caps - query AMP capabilities * @codec: the HD-auio codec * @nid: the NID to query * @direction: either #HDA_INPUT or #HDA_OUTPUT * * Query AMP capabilities for the given widget and direction. * Returns the obtained capability bits. * * When cap bits have been already read, this doesn't read again but * returns the cached value. */ u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction) { return query_caps_hash(codec, nid, direction, HDA_HASH_KEY(nid, direction, 0), read_amp_cap); } EXPORT_SYMBOL_HDA(query_amp_caps); /** * snd_hda_override_amp_caps - Override the AMP capabilities * @codec: the CODEC to clean up * @nid: the NID to clean up * @direction: either #HDA_INPUT or #HDA_OUTPUT * @caps: the capability bits to set * * Override the cached AMP caps bits value by the given one. * This function is useful if the driver needs to adjust the AMP ranges, * e.g. limit to 0dB, etc. * * Returns zero if successful or a negative error code. */ int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir, unsigned int caps) { return write_caps_hash(codec, HDA_HASH_KEY(nid, dir, 0), caps); } EXPORT_SYMBOL_HDA(snd_hda_override_amp_caps); static unsigned int read_pin_cap(struct hda_codec *codec, hda_nid_t nid, int dir) { return snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP); } /** * snd_hda_query_pin_caps - Query PIN capabilities * @codec: the HD-auio codec * @nid: the NID to query * * Query PIN capabilities for the given widget. * Returns the obtained capability bits. * * When cap bits have been already read, this doesn't read again but * returns the cached value. */ u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid) { return query_caps_hash(codec, nid, 0, HDA_HASH_PINCAP_KEY(nid), read_pin_cap); } EXPORT_SYMBOL_HDA(snd_hda_query_pin_caps); /** * snd_hda_override_pin_caps - Override the pin capabilities * @codec: the CODEC * @nid: the NID to override * @caps: the capability bits to set * * Override the cached PIN capabilitiy bits value by the given one. * * Returns zero if successful or a negative error code. */ int snd_hda_override_pin_caps(struct hda_codec *codec, hda_nid_t nid, unsigned int caps) { return write_caps_hash(codec, HDA_HASH_PINCAP_KEY(nid), caps); } EXPORT_SYMBOL_HDA(snd_hda_override_pin_caps); /* read or sync the hash value with the current value; * call within hash_mutex */ static struct hda_amp_info * update_amp_hash(struct hda_codec *codec, hda_nid_t nid, int ch, int direction, int index) { struct hda_amp_info *info; unsigned int parm, val = 0; bool val_read = false; retry: info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, index)); if (!info) return NULL; if (!(info->head.val & INFO_AMP_VOL(ch))) { if (!val_read) { mutex_unlock(&codec->hash_mutex); parm = ch ? AC_AMP_GET_RIGHT : AC_AMP_GET_LEFT; parm |= direction == HDA_OUTPUT ? AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT; parm |= index; val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, parm); val &= 0xff; val_read = true; mutex_lock(&codec->hash_mutex); goto retry; } info->vol[ch] = val; info->head.val |= INFO_AMP_VOL(ch); } return info; } /* * write the current volume in info to the h/w */ static void put_vol_mute(struct hda_codec *codec, struct hda_amp_info *info, hda_nid_t nid, int ch, int direction, int index, int val) { u32 parm; parm = ch ? AC_AMP_SET_RIGHT : AC_AMP_SET_LEFT; parm |= direction == HDA_OUTPUT ? AC_AMP_SET_OUTPUT : AC_AMP_SET_INPUT; parm |= index << AC_AMP_SET_INDEX_SHIFT; if ((val & HDA_AMP_MUTE) && !(info->amp_caps & AC_AMPCAP_MUTE) && (info->amp_caps & AC_AMPCAP_MIN_MUTE)) ; /* set the zero value as a fake mute */ else parm |= val; snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, parm); } /** * snd_hda_codec_amp_read - Read AMP value * @codec: HD-audio codec * @nid: NID to read the AMP value * @ch: channel (left=0 or right=1) * @direction: #HDA_INPUT or #HDA_OUTPUT * @index: the index value (only for input direction) * * Read AMP value. The volume is between 0 to 0x7f, 0x80 = mute bit. */ int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch, int direction, int index) { struct hda_amp_info *info; unsigned int val = 0; mutex_lock(&codec->hash_mutex); info = update_amp_hash(codec, nid, ch, direction, index); if (info) val = info->vol[ch]; mutex_unlock(&codec->hash_mutex); return val; } EXPORT_SYMBOL_HDA(snd_hda_codec_amp_read); /** * snd_hda_codec_amp_update - update the AMP value * @codec: HD-audio codec * @nid: NID to read the AMP value * @ch: channel (left=0 or right=1) * @direction: #HDA_INPUT or #HDA_OUTPUT * @idx: the index value (only for input direction) * @mask: bit mask to set * @val: the bits value to set * * Update the AMP value with a bit mask. * Returns 0 if the value is unchanged, 1 if changed. */ int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, int direction, int idx, int mask, int val) { struct hda_amp_info *info; if (snd_BUG_ON(mask & ~0xff)) mask &= 0xff; val &= mask; mutex_lock(&codec->hash_mutex); info = update_amp_hash(codec, nid, ch, direction, idx); if (!info) { mutex_unlock(&codec->hash_mutex); return 0; } val |= info->vol[ch] & ~mask; if (info->vol[ch] == val) { mutex_unlock(&codec->hash_mutex); return 0; } info->vol[ch] = val; mutex_unlock(&codec->hash_mutex); put_vol_mute(codec, info, nid, ch, direction, idx, val); return 1; } EXPORT_SYMBOL_HDA(snd_hda_codec_amp_update); /** * snd_hda_codec_amp_stereo - update the AMP stereo values * @codec: HD-audio codec * @nid: NID to read the AMP value * @direction: #HDA_INPUT or #HDA_OUTPUT * @idx: the index value (only for input direction) * @mask: bit mask to set * @val: the bits value to set * * Update the AMP values like snd_hda_codec_amp_update(), but for a * stereo widget with the same mask and value. */ int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid, int direction, int idx, int mask, int val) { int ch, ret = 0; if (snd_BUG_ON(mask & ~0xff)) mask &= 0xff; for (ch = 0; ch < 2; ch++) ret |= snd_hda_codec_amp_update(codec, nid, ch, direction, idx, mask, val); return ret; } EXPORT_SYMBOL_HDA(snd_hda_codec_amp_stereo); #ifdef CONFIG_PM /** * snd_hda_codec_resume_amp - Resume all AMP commands from the cache * @codec: HD-audio codec * * Resume the all amp commands from the cache. */ void snd_hda_codec_resume_amp(struct hda_codec *codec) { struct hda_amp_info *buffer = codec->amp_cache.buf.list; int i; for (i = 0; i < codec->amp_cache.buf.used; i++, buffer++) { u32 key = buffer->head.key; hda_nid_t nid; unsigned int idx, dir, ch; if (!key) continue; nid = key & 0xff; idx = (key >> 16) & 0xff; dir = (key >> 24) & 0xff; for (ch = 0; ch < 2; ch++) { if (!(buffer->head.val & INFO_AMP_VOL(ch))) continue; put_vol_mute(codec, buffer, nid, ch, dir, idx, buffer->vol[ch]); } } } EXPORT_SYMBOL_HDA(snd_hda_codec_resume_amp); #endif /* CONFIG_PM */ static u32 get_amp_max_value(struct hda_codec *codec, hda_nid_t nid, int dir, unsigned int ofs) { u32 caps = query_amp_caps(codec, nid, dir); /* get num steps */ caps = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT; if (ofs < caps) caps -= ofs; return caps; } /** * snd_hda_mixer_amp_volume_info - Info callback for a standard AMP mixer * * The control element is supposed to have the private_value field * set up via HDA_COMPOSE_AMP_VAL*() or related macros. */ int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); u16 nid = get_amp_nid(kcontrol); u8 chs = get_amp_channels(kcontrol); int dir = get_amp_direction(kcontrol); unsigned int ofs = get_amp_offset(kcontrol); uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = chs == 3 ? 2 : 1; uinfo->value.integer.min = 0; uinfo->value.integer.max = get_amp_max_value(codec, nid, dir, ofs); if (!uinfo->value.integer.max) { printk(KERN_WARNING "hda_codec: " "num_steps = 0 for NID=0x%x (ctl = %s)\n", nid, kcontrol->id.name); return -EINVAL; } return 0; } EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_volume_info); static inline unsigned int read_amp_value(struct hda_codec *codec, hda_nid_t nid, int ch, int dir, int idx, unsigned int ofs) { unsigned int val; val = snd_hda_codec_amp_read(codec, nid, ch, dir, idx); val &= HDA_AMP_VOLMASK; if (val >= ofs) val -= ofs; else val = 0; return val; } static inline int update_amp_value(struct hda_codec *codec, hda_nid_t nid, int ch, int dir, int idx, unsigned int ofs, unsigned int val) { unsigned int maxval; if (val > 0) val += ofs; /* ofs = 0: raw max value */ maxval = get_amp_max_value(codec, nid, dir, 0); if (val > maxval) val = maxval; return snd_hda_codec_amp_update(codec, nid, ch, dir, idx, HDA_AMP_VOLMASK, val); } /** * snd_hda_mixer_amp_volume_get - Get callback for a standard AMP mixer volume * * The control element is supposed to have the private_value field * set up via HDA_COMPOSE_AMP_VAL*() or related macros. */ int snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); hda_nid_t nid = get_amp_nid(kcontrol); int chs = get_amp_channels(kcontrol); int dir = get_amp_direction(kcontrol); int idx = get_amp_index(kcontrol); unsigned int ofs = get_amp_offset(kcontrol); long *valp = ucontrol->value.integer.value; if (chs & 1) *valp++ = read_amp_value(codec, nid, 0, dir, idx, ofs); if (chs & 2) *valp = read_amp_value(codec, nid, 1, dir, idx, ofs); return 0; } EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_volume_get); /** * snd_hda_mixer_amp_volume_put - Put callback for a standard AMP mixer volume * * The control element is supposed to have the private_value field * set up via HDA_COMPOSE_AMP_VAL*() or related macros. */ int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); hda_nid_t nid = get_amp_nid(kcontrol); int chs = get_amp_channels(kcontrol); int dir = get_amp_direction(kcontrol); int idx = get_amp_index(kcontrol); unsigned int ofs = get_amp_offset(kcontrol); long *valp = ucontrol->value.integer.value; int change = 0; snd_hda_power_up(codec); if (chs & 1) { change = update_amp_value(codec, nid, 0, dir, idx, ofs, *valp); valp++; } if (chs & 2) change |= update_amp_value(codec, nid, 1, dir, idx, ofs, *valp); snd_hda_power_down(codec); return change; } EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_volume_put); /** * snd_hda_mixer_amp_volume_put - TLV callback for a standard AMP mixer volume * * The control element is supposed to have the private_value field * set up via HDA_COMPOSE_AMP_VAL*() or related macros. */ int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag, unsigned int size, unsigned int __user *_tlv) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); hda_nid_t nid = get_amp_nid(kcontrol); int dir = get_amp_direction(kcontrol); unsigned int ofs = get_amp_offset(kcontrol); bool min_mute = get_amp_min_mute(kcontrol); u32 caps, val1, val2; if (size < 4 * sizeof(unsigned int)) return -ENOMEM; caps = query_amp_caps(codec, nid, dir); val2 = (caps & AC_AMPCAP_STEP_SIZE) >> AC_AMPCAP_STEP_SIZE_SHIFT; val2 = (val2 + 1) * 25; val1 = -((caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT); val1 += ofs; val1 = ((int)val1) * ((int)val2); if (min_mute || (caps & AC_AMPCAP_MIN_MUTE)) val2 |= TLV_DB_SCALE_MUTE; if (put_user(SNDRV_CTL_TLVT_DB_SCALE, _tlv)) return -EFAULT; if (put_user(2 * sizeof(unsigned int), _tlv + 1)) return -EFAULT; if (put_user(val1, _tlv + 2)) return -EFAULT; if (put_user(val2, _tlv + 3)) return -EFAULT; return 0; } EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_tlv); /** * snd_hda_set_vmaster_tlv - Set TLV for a virtual master control * @codec: HD-audio codec * @nid: NID of a reference widget * @dir: #HDA_INPUT or #HDA_OUTPUT * @tlv: TLV data to be stored, at least 4 elements * * Set (static) TLV data for a virtual master volume using the AMP caps * obtained from the reference NID. * The volume range is recalculated as if the max volume is 0dB. */ void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir, unsigned int *tlv) { u32 caps; int nums, step; caps = query_amp_caps(codec, nid, dir); nums = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT; step = (caps & AC_AMPCAP_STEP_SIZE) >> AC_AMPCAP_STEP_SIZE_SHIFT; step = (step + 1) * 25; tlv[0] = SNDRV_CTL_TLVT_DB_SCALE; tlv[1] = 2 * sizeof(unsigned int); tlv[2] = -nums * step; tlv[3] = step; } EXPORT_SYMBOL_HDA(snd_hda_set_vmaster_tlv); /* find a mixer control element with the given name */ static struct snd_kcontrol * _snd_hda_find_mixer_ctl(struct hda_codec *codec, const char *name, int idx) { struct snd_ctl_elem_id id; memset(&id, 0, sizeof(id)); id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; id.index = idx; if (snd_BUG_ON(strlen(name) >= sizeof(id.name))) return NULL; strcpy(id.name, name); return snd_ctl_find_id(codec->bus->card, &id); } /** * snd_hda_find_mixer_ctl - Find a mixer control element with the given name * @codec: HD-audio codec * @name: ctl id name string * * Get the control element with the given id string and IFACE_MIXER. */ struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec, const char *name) { return _snd_hda_find_mixer_ctl(codec, name, 0); } EXPORT_SYMBOL_HDA(snd_hda_find_mixer_ctl); static int find_empty_mixer_ctl_idx(struct hda_codec *codec, const char *name) { int idx; for (idx = 0; idx < 16; idx++) { /* 16 ctlrs should be large enough */ if (!_snd_hda_find_mixer_ctl(codec, name, idx)) return idx; } return -EBUSY; } /** * snd_hda_ctl_add - Add a control element and assign to the codec * @codec: HD-audio codec * @nid: corresponding NID (optional) * @kctl: the control element to assign * * Add the given control element to an array inside the codec instance. * All control elements belonging to a codec are supposed to be added * by this function so that a proper clean-up works at the free or * reconfiguration time. * * If non-zero @nid is passed, the NID is assigned to the control element. * The assignment is shown in the codec proc file. * * snd_hda_ctl_add() checks the control subdev id field whether * #HDA_SUBDEV_NID_FLAG bit is set. If set (and @nid is zero), the lower * bits value is taken as the NID to assign. The #HDA_NID_ITEM_AMP bit * specifies if kctl->private_value is a HDA amplifier value. */ int snd_hda_ctl_add(struct hda_codec *codec, hda_nid_t nid, struct snd_kcontrol *kctl) { int err; unsigned short flags = 0; struct hda_nid_item *item; if (kctl->id.subdevice & HDA_SUBDEV_AMP_FLAG) { flags |= HDA_NID_ITEM_AMP; if (nid == 0) nid = get_amp_nid_(kctl->private_value); } if ((kctl->id.subdevice & HDA_SUBDEV_NID_FLAG) != 0 && nid == 0) nid = kctl->id.subdevice & 0xffff; if (kctl->id.subdevice & (HDA_SUBDEV_NID_FLAG|HDA_SUBDEV_AMP_FLAG)) kctl->id.subdevice = 0; err = snd_ctl_add(codec->bus->card, kctl); if (err < 0) return err; item = snd_array_new(&codec->mixers); if (!item) return -ENOMEM; item->kctl = kctl; item->nid = nid; item->flags = flags; return 0; } EXPORT_SYMBOL_HDA(snd_hda_ctl_add); /** * snd_hda_add_nid - Assign a NID to a control element * @codec: HD-audio codec * @nid: corresponding NID (optional) * @kctl: the control element to assign * @index: index to kctl * * Add the given control element to an array inside the codec instance. * This function is used when #snd_hda_ctl_add cannot be used for 1:1 * NID:KCTL mapping - for example "Capture Source" selector. */ int snd_hda_add_nid(struct hda_codec *codec, struct snd_kcontrol *kctl, unsigned int index, hda_nid_t nid) { struct hda_nid_item *item; if (nid > 0) { item = snd_array_new(&codec->nids); if (!item) return -ENOMEM; item->kctl = kctl; item->index = index; item->nid = nid; return 0; } printk(KERN_ERR "hda-codec: no NID for mapping control %s:%d:%d\n", kctl->id.name, kctl->id.index, index); return -EINVAL; } EXPORT_SYMBOL_HDA(snd_hda_add_nid); /** * snd_hda_ctls_clear - Clear all controls assigned to the given codec * @codec: HD-audio codec */ void snd_hda_ctls_clear(struct hda_codec *codec) { int i; struct hda_nid_item *items = codec->mixers.list; for (i = 0; i < codec->mixers.used; i++) snd_ctl_remove(codec->bus->card, items[i].kctl); snd_array_free(&codec->mixers); snd_array_free(&codec->nids); } /* pseudo device locking * toggle card->shutdown to allow/disallow the device access (as a hack) */ int snd_hda_lock_devices(struct hda_bus *bus) { struct snd_card *card = bus->card; struct hda_codec *codec; spin_lock(&card->files_lock); if (card->shutdown) goto err_unlock; card->shutdown = 1; if (!list_empty(&card->ctl_files)) goto err_clear; list_for_each_entry(codec, &bus->codec_list, list) { int pcm; for (pcm = 0; pcm < codec->num_pcms; pcm++) { struct hda_pcm *cpcm = &codec->pcm_info[pcm]; if (!cpcm->pcm) continue; if (cpcm->pcm->streams[0].substream_opened || cpcm->pcm->streams[1].substream_opened) goto err_clear; } } spin_unlock(&card->files_lock); return 0; err_clear: card->shutdown = 0; err_unlock: spin_unlock(&card->files_lock); return -EINVAL; } EXPORT_SYMBOL_HDA(snd_hda_lock_devices); void snd_hda_unlock_devices(struct hda_bus *bus) { struct snd_card *card = bus->card; card = bus->card; spin_lock(&card->files_lock); card->shutdown = 0; spin_unlock(&card->files_lock); } EXPORT_SYMBOL_HDA(snd_hda_unlock_devices); /** * snd_hda_codec_reset - Clear all objects assigned to the codec * @codec: HD-audio codec * * This frees the all PCM and control elements assigned to the codec, and * clears the caches and restores the pin default configurations. * * When a device is being used, it returns -EBSY. If successfully freed, * returns zero. */ int snd_hda_codec_reset(struct hda_codec *codec) { struct hda_bus *bus = codec->bus; struct snd_card *card = bus->card; int i; if (snd_hda_lock_devices(bus) < 0) return -EBUSY; /* OK, let it free */ #ifdef CONFIG_SND_HDA_POWER_SAVE cancel_delayed_work_sync(&codec->power_work); codec->power_on = 0; codec->power_transition = 0; codec->power_jiffies = jiffies; flush_workqueue(bus->workq); #endif snd_hda_ctls_clear(codec); /* relase PCMs */ for (i = 0; i < codec->num_pcms; i++) { if (codec->pcm_info[i].pcm) { snd_device_free(card, codec->pcm_info[i].pcm); clear_bit(codec->pcm_info[i].device, bus->pcm_dev_bits); } } if (codec->patch_ops.free) codec->patch_ops.free(codec); snd_hda_jack_tbl_clear(codec); codec->proc_widget_hook = NULL; codec->spec = NULL; free_hda_cache(&codec->amp_cache); free_hda_cache(&codec->cmd_cache); init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info)); init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head)); /* free only driver_pins so that init_pins + user_pins are restored */ snd_array_free(&codec->driver_pins); restore_pincfgs(codec); codec->num_pcms = 0; codec->pcm_info = NULL; codec->preset = NULL; memset(&codec->patch_ops, 0, sizeof(codec->patch_ops)); codec->slave_dig_outs = NULL; codec->spdif_status_reset = 0; module_put(codec->owner); codec->owner = NULL; /* allow device access again */ snd_hda_unlock_devices(bus); return 0; } typedef int (*map_slave_func_t)(void *, struct snd_kcontrol *); /* apply the function to all matching slave ctls in the mixer list */ static int map_slaves(struct hda_codec *codec, const char * const *slaves, const char *suffix, map_slave_func_t func, void *data) { struct hda_nid_item *items; const char * const *s; int i, err; items = codec->mixers.list; for (i = 0; i < codec->mixers.used; i++) { struct snd_kcontrol *sctl = items[i].kctl; if (!sctl || !sctl->id.name || sctl->id.iface != SNDRV_CTL_ELEM_IFACE_MIXER) continue; for (s = slaves; *s; s++) { char tmpname[sizeof(sctl->id.name)]; const char *name = *s; if (suffix) { snprintf(tmpname, sizeof(tmpname), "%s %s", name, suffix); name = tmpname; } if (!strcmp(sctl->id.name, name)) { err = func(data, sctl); if (err) return err; break; } } } return 0; } static int check_slave_present(void *data, struct snd_kcontrol *sctl) { return 1; } /* guess the value corresponding to 0dB */ static int get_kctl_0dB_offset(struct snd_kcontrol *kctl) { int _tlv[4]; const int *tlv = NULL; int val = -1; if (kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) { /* FIXME: set_fs() hack for obtaining user-space TLV data */ mm_segment_t fs = get_fs(); set_fs(get_ds()); if (!kctl->tlv.c(kctl, 0, sizeof(_tlv), _tlv)) tlv = _tlv; set_fs(fs); } else if (kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_READ) tlv = kctl->tlv.p; if (tlv && tlv[0] == SNDRV_CTL_TLVT_DB_SCALE) val = -tlv[2] / tlv[3]; return val; } /* call kctl->put with the given value(s) */ static int put_kctl_with_value(struct snd_kcontrol *kctl, int val) { struct snd_ctl_elem_value *ucontrol; ucontrol = kzalloc(sizeof(*ucontrol), GFP_KERNEL); if (!ucontrol) return -ENOMEM; ucontrol->value.integer.value[0] = val; ucontrol->value.integer.value[1] = val; kctl->put(kctl, ucontrol); kfree(ucontrol); return 0; } /* initialize the slave volume with 0dB */ static int init_slave_0dB(void *data, struct snd_kcontrol *slave) { int offset = get_kctl_0dB_offset(slave); if (offset > 0) put_kctl_with_value(slave, offset);