diff options
-rw-r--r-- | Documentation/sound/alsa/ALSA-Configuration.txt | 4 | ||||
-rw-r--r-- | Documentation/sound/alsa/HD-Audio-Models.txt | 33 | ||||
-rw-r--r-- | Documentation/sound/alsa/HD-Audio.txt | 64 | ||||
-rw-r--r-- | sound/pci/hda/Kconfig | 27 | ||||
-rw-r--r-- | sound/pci/hda/Makefile | 4 | ||||
-rw-r--r-- | sound/pci/hda/hda_beep.c | 4 | ||||
-rw-r--r-- | sound/pci/hda/hda_codec.c | 68 | ||||
-rw-r--r-- | sound/pci/hda/hda_codec.h | 10 | ||||
-rw-r--r-- | sound/pci/hda/hda_generic.c | 18 | ||||
-rw-r--r-- | sound/pci/hda/hda_hwdep.c | 236 | ||||
-rw-r--r-- | sound/pci/hda/hda_intel.c | 74 | ||||
-rw-r--r-- | sound/pci/hda/hda_local.h | 14 | ||||
-rw-r--r-- | sound/pci/hda/hda_proc.c | 7 | ||||
-rw-r--r-- | sound/pci/hda/patch_analog.c | 131 | ||||
-rw-r--r-- | sound/pci/hda/patch_atihdmi.c | 3 | ||||
-rw-r--r-- | sound/pci/hda/patch_ca0110.c | 3 | ||||
-rw-r--r-- | sound/pci/hda/patch_cirrus.c | 1194 | ||||
-rw-r--r-- | sound/pci/hda/patch_cmedia.c | 3 | ||||
-rw-r--r-- | sound/pci/hda/patch_conexant.c | 479 | ||||
-rw-r--r-- | sound/pci/hda/patch_intelhdmi.c | 104 | ||||
-rw-r--r-- | sound/pci/hda/patch_nvhdmi.c | 2 | ||||
-rw-r--r-- | sound/pci/hda/patch_realtek.c | 4144 | ||||
-rw-r--r-- | sound/pci/hda/patch_sigmatel.c | 1206 | ||||
-rw-r--r-- | sound/pci/hda/patch_via.c | 3 |
24 files changed, 4922 insertions, 2913 deletions
diff --git a/Documentation/sound/alsa/ALSA-Configuration.txt b/Documentation/sound/alsa/ALSA-Configuration.txt index ea16e7d184a..ad51c93d41e 100644 --- a/Documentation/sound/alsa/ALSA-Configuration.txt +++ b/Documentation/sound/alsa/ALSA-Configuration.txt @@ -788,6 +788,10 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. bdl_pos_adj - Specifies the DMA IRQ timing delay in samples. Passing -1 will make the driver to choose the appropriate value based on the controller chip. + patch - Specifies the early "patch" files to modify the HD-audio + setup before initializing the codecs. This option is + available only when CONFIG_SND_HDA_PATCH_LOADER=y is set. + See HD-Audio.txt for details. [Single (global) options] single_cmd - Use single immediate commands to communicate with diff --git a/Documentation/sound/alsa/HD-Audio-Models.txt b/Documentation/sound/alsa/HD-Audio-Models.txt index 939a3dd5814..97eebd63bed 100644 --- a/Documentation/sound/alsa/HD-Audio-Models.txt +++ b/Documentation/sound/alsa/HD-Audio-Models.txt @@ -114,8 +114,8 @@ ALC662/663/272 samsung-nc10 Samsung NC10 mini notebook auto auto-config reading BIOS (default) -ALC882/885 -========== +ALC882/883/885/888/889 +====================== 3stack-dig 3-jack with SPDIF I/O 6stack-dig 6-jack digital with SPDIF I/O arima Arima W820Di1 @@ -127,12 +127,8 @@ ALC882/885 mbp3 Macbook Pro rev3 imac24 iMac 24'' with jack detection w2jc ASUS W2JC - auto auto-config reading BIOS (default) - -ALC883/888 -========== - 3stack-dig 3-jack with SPDIF I/O - 6stack-dig 6-jack digital with SPDIF I/O + 3stack-2ch-dig 3-jack with SPDIF I/O (ALC883) + alc883-6stack-dig 6-jack digital with SPDIF I/O (ALC883) 3stack-6ch 3-jack 6-channel 3stack-6ch-dig 3-jack 6-channel with SPDIF I/O 6stack-dig-demo 6-jack digital for Intel demo board @@ -140,6 +136,7 @@ ALC883/888 acer-aspire Acer Aspire 9810 acer-aspire-4930g Acer Aspire 4930G acer-aspire-6530g Acer Aspire 6530G + acer-aspire-7730g Acer Aspire 7730G acer-aspire-8930g Acer Aspire 8930G medion Medion Laptops medion-md2 Medion MD2 @@ -155,10 +152,13 @@ ALC883/888 3stack-hp HP machines with 3stack (Lucknow, Samba boards) 6stack-dell Dell machines with 6stack (Inspiron 530) mitac Mitac 8252D + clevo-m540r Clevo M540R (6ch + digital) clevo-m720 Clevo M720 laptop series fujitsu-pi2515 Fujitsu AMILO Pi2515 fujitsu-xa3530 Fujitsu AMILO XA3530 3stack-6ch-intel Intel DG33* boards + intel-alc889a Intel IbexPeak with ALC889A + intel-x58 Intel DX58 with ALC889 asus-p5q ASUS P5Q-EM boards mb31 MacBook 3,1 sony-vaio-tt Sony VAIO TT @@ -229,7 +229,7 @@ AD1984 ====== basic default configuration thinkpad Lenovo Thinkpad T61/X61 - dell Dell T3400 + dell_desktop Dell T3400 AD1986A ======= @@ -258,6 +258,7 @@ Conexant 5045 laptop-micsense Laptop with Mic sense (old model fujitsu) laptop-hpmicsense Laptop with HP and Mic senses benq Benq R55E + laptop-hp530 HP 530 laptop test for testing/debugging purpose, almost all controls can be adjusted. Appearing only when compiled with $CONFIG_SND_DEBUG=y @@ -278,9 +279,16 @@ Conexant 5051 hp-dv6736 HP dv6736 lenovo-x200 Lenovo X200 laptop +Conexant 5066 +============= + laptop Basic Laptop config (default) + dell-laptop Dell laptops + olpc-xo-1_5 OLPC XO 1.5 + STAC9200 ======== ref Reference board + oqo OQO Model 2 dell-d21 Dell (unknown) dell-d22 Dell (unknown) dell-d23 Dell (unknown) @@ -368,10 +376,12 @@ STAC92HD73* =========== ref Reference board no-jd BIOS setup but without jack-detection + intel Intel DG45* mobos dell-m6-amic Dell desktops/laptops with analog mics dell-m6-dmic Dell desktops/laptops with digital mics dell-m6 Dell desktops/laptops with both type of mics dell-eq Dell desktops/laptops + alienware Alienware M17x auto BIOS setup (default) STAC92HD83* @@ -385,3 +395,8 @@ STAC9872 ======== vaio VAIO laptop without SPDIF auto BIOS setup (default) + +Cirrus Logic CS4206/4207 +======================== + mbp55 MacBook Pro 5,5 + auto BIOS setup (default) diff --git a/Documentation/sound/alsa/HD-Audio.txt b/Documentation/sound/alsa/HD-Audio.txt index 71ac995b191..7b8a5f947d1 100644 --- a/Documentation/sound/alsa/HD-Audio.txt +++ b/Documentation/sound/alsa/HD-Audio.txt @@ -139,6 +139,10 @@ The driver checks PCI SSID and looks through the static configuration table until any matching entry is found. If you have a new machine, you may see a message like below: ------------------------------------------------------------------------ + hda_codec: ALC880: BIOS auto-probing. +------------------------------------------------------------------------ +Meanwhile, in the earlier versions, you would see a message like: +------------------------------------------------------------------------ hda_codec: Unknown model for ALC880, trying auto-probe from BIOS... ------------------------------------------------------------------------ Even if you see such a message, DON'T PANIC. Take a deep breath and @@ -403,6 +407,66 @@ re-configure based on that state, run like below: ------------------------------------------------------------------------ +Early Patching +~~~~~~~~~~~~~~ +When CONFIG_SND_HDA_PATCH_LOADER=y is set, you can pass a "patch" as a +firmware file for modifying the HD-audio setup before initializing the +codec. This can work basically like the reconfiguration via sysfs in +the above, but it does it before the first codec configuration. + +A patch file is a plain text file which looks like below: + +------------------------------------------------------------------------ + [codec] + 0x12345678 0xabcd1234 2 + + [model] + auto + + [pincfg] + 0x12 0x411111f0 + + [verb] + 0x20 0x500 0x03 + 0x20 0x400 0xff + + [hint] + hp_detect = yes +------------------------------------------------------------------------ + +The file needs to have a line `[codec]`. The next line should contain +three numbers indicating the codec vendor-id (0x12345678 in the +example), the codec subsystem-id (0xabcd1234) and the address (2) of +the codec. The rest patch entries are applied to this specified codec +until another codec entry is given. + +The `[model]` line allows to change the model name of the each codec. +In the example above, it will be changed to model=auto. +Note that this overrides the module option. + +After the `[pincfg]` line, the contents are parsed as the initial +default pin-configurations just like `user_pin_configs` sysfs above. +The values can be shown in user_pin_configs sysfs file, too. + +Similarly, the lines after `[verb]` are parsed as `init_verbs` +sysfs entries, and the lines after `[hint]` are parsed as `hints` +sysfs entries, respectively. + +The hd-audio driver reads the file via request_firmware(). Thus, +a patch file has to be located on the appropriate firmware path, +typically, /lib/firmware. For example, when you pass the option +`patch=hda-init.fw`, the file /lib/firmware/hda-init-fw must be +present. + +The patch module option is specific to each card instance, and you +need to give one file name for each instance, separated by commas. +For example, if you have two cards, one for an on-board analog and one +for an HDMI video board, you may pass patch option like below: +------------------------------------------------------------------------ + options snd-hda-intel patch=on-board-patch,hdmi-patch +------------------------------------------------------------------------ + + Power-Saving ~~~~~~~~~~~~ The power-saving is a kind of auto-suspend of the device. When the diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 04438f1d682..55545e0818b 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -46,6 +46,20 @@ config SND_HDA_INPUT_JACK Say Y here to enable the jack plugging notification via input layer. +config SND_HDA_PATCH_LOADER + bool "Support initialization patch loading for HD-audio" + depends on EXPERIMENTAL + select FW_LOADER + select SND_HDA_HWDEP + select SND_HDA_RECONFIG + help + Say Y here to allow the HD-audio driver to load a pseudo + firmware file ("patch") for overriding the BIOS setup at + start up. The "patch" file can be specified via patch module + option, such as patch=hda-init. + + This option turns on hwdep and reconfig features automatically. + config SND_HDA_CODEC_REALTEK bool "Build Realtek HD-audio codec support" default y @@ -134,6 +148,19 @@ config SND_HDA_ELD def_bool y depends on SND_HDA_CODEC_INTELHDMI +config SND_HDA_CODEC_CIRRUS + bool "Build Cirrus Logic codec support" + depends on SND_HDA_INTEL + default y + help + Say Y here to include Cirrus Logic codec support in + snd-hda-intel driver, such as CS4206. + + When the HD-audio driver is built as a module, the codec + support code is also built as another module, + snd-hda-codec-cirrus. + This module is automatically loaded at probing. + config SND_HDA_CODEC_CONEXANT bool "Build Conexant HD-audio codec support" default y diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile index e3081d4586c..315a1c4f899 100644 --- a/sound/pci/hda/Makefile +++ b/sound/pci/hda/Makefile @@ -13,6 +13,7 @@ snd-hda-codec-analog-objs := patch_analog.o snd-hda-codec-idt-objs := patch_sigmatel.o snd-hda-codec-si3054-objs := patch_si3054.o snd-hda-codec-atihdmi-objs := patch_atihdmi.o +snd-hda-codec-cirrus-objs := patch_cirrus.o snd-hda-codec-ca0110-objs := patch_ca0110.o snd-hda-codec-conexant-objs := patch_conexant.o snd-hda-codec-via-objs := patch_via.o @@ -41,6 +42,9 @@ endif ifdef CONFIG_SND_HDA_CODEC_ATIHDMI obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-atihdmi.o endif +ifdef CONFIG_SND_HDA_CODEC_CIRRUS +obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-cirrus.o +endif ifdef CONFIG_SND_HDA_CODEC_CA0110 obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-ca0110.o endif diff --git a/sound/pci/hda/hda_beep.c b/sound/pci/hda/hda_beep.c index b0275a05087..3f51a981e60 100644 --- a/sound/pci/hda/hda_beep.c +++ b/sound/pci/hda/hda_beep.c @@ -24,6 +24,7 @@ #include <linux/workqueue.h> #include <sound/core.h> #include "hda_beep.h" +#include "hda_local.h" enum { DIGBEEP_HZ_STEP = 46875, /* 46.875 Hz */ @@ -118,6 +119,9 @@ int snd_hda_attach_beep_device(struct hda_codec *codec, int nid) struct hda_beep *beep; int err; + if (!snd_hda_get_bool_hint(codec, "beep")) + return 0; /* disabled explicitly */ + beep = kzalloc(sizeof(*beep), GFP_KERNEL); if (beep == NULL) return -ENOMEM; diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index c7df01b72ca..af989f660cc 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -44,6 +44,7 @@ struct hda_vendor_id { /* codec vendor labels */ static struct hda_vendor_id hda_vendor_ids[] = { { 0x1002, "ATI" }, + { 0x1013, "Cirrus Logic" }, { 0x1057, "Motorola" }, { 0x1095, "Silicon Image" }, { 0x10de, "Nvidia" }, @@ -150,7 +151,14 @@ make_codec_cmd(struct hda_codec *codec, hda_nid_t nid, int direct, { u32 val; - val = (u32)(codec->addr & 0x0f) << 28; + 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; @@ -167,6 +175,9 @@ static int codec_exec_verb(struct hda_codec *codec, unsigned int cmd, struct hda_bus *bus = codec->bus; int err; + if (cmd == ~0) + return -1; + if (res) *res = -1; again: @@ -291,11 +302,20 @@ int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid, 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) { + snd_printk(KERN_WARNING "hda_codec: " + "connection list not available for 0x%x\n", nid); + return -EINVAL; + } + parm = snd_hda_param_read(codec, nid, AC_PAR_CONNLIST_LEN); if (parm & AC_CLIST_LONG) { /* long form */ @@ -316,6 +336,8 @@ int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid, /* 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; } @@ -327,9 +349,12 @@ int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid, int range_val; hda_nid_t val, n; - if (i % num_elems == 0) + 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) { @@ -727,8 +752,7 @@ static int read_pin_defaults(struct hda_codec *codec) for (i = 0; i < codec->num_nodes; i++, nid++) { struct hda_pincfg *pin; unsigned int wcaps = get_wcaps(codec, nid); - unsigned int wid_type = (wcaps & AC_WCAP_TYPE) >> - AC_WCAP_TYPE_SHIFT; + unsigned int wid_type = get_wcaps_type(wcaps); if (wid_type != AC_WID_PIN) continue; pin = snd_array_new(&codec->init_pins); @@ -891,7 +915,7 @@ static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg, * Returns 0 if successful, or a negative error code. */ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr, - int do_init, struct hda_codec **codecp) + struct hda_codec **codecp) { struct hda_codec *codec; char component[31]; @@ -984,11 +1008,6 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr codec->afg ? codec->afg : codec->mfg, AC_PWRST_D0); - if (do_init) { - err = snd_hda_codec_configure(codec); - if (err < 0) - goto error; - } snd_hda_codec_proc_new(codec); snd_hda_create_hwdep(codec); @@ -1042,6 +1061,7 @@ int snd_hda_codec_configure(struct hda_codec *codec) err = init_unsol_queue(codec->bus); return err; } +EXPORT_SYMBOL_HDA(snd_hda_codec_configure); /** * snd_hda_codec_setup_stream - set up the codec for streaming @@ -2356,16 +2376,20 @@ static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg, hda_nid_t nid; int i; - snd_hda_codec_write(codec, fg, 0, AC_VERB_SET_POWER_STATE, + /* this delay seems necessary to avoid click noise at power-down */ + if (power_state == AC_PWRST_D3) + msleep(100); + snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE, power_state); - msleep(10); /* partial workaround for "azx_get_response timeout" */ + /* partial workaround for "azx_get_response timeout" */ + if (power_state == AC_PWRST_D0) + msleep(10); nid = codec->start_nid; for (i = 0; i < codec->num_nodes; i++, nid++) { unsigned int wcaps = get_wcaps(codec, nid); if (wcaps & AC_WCAP_POWER) { - unsigned int wid_type = (wcaps & AC_WCAP_TYPE) >> - AC_WCAP_TYPE_SHIFT; + unsigned int wid_type = get_wcaps_type(wcaps); if (power_state == AC_PWRST_D3 && wid_type == AC_WID_PIN) { unsigned int pincap; @@ -2573,7 +2597,7 @@ unsigned int snd_hda_calc_stream_format(unsigned int rate, case 20: case 24: case 32: - if (maxbps >= 32) + if (maxbps >= 32 || format == SNDRV_PCM_FORMAT_FLOAT_LE) val |= 0x40; else if (maxbps >= 24) val |= 0x30; @@ -2700,11 +2724,12 @@ static int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid, bps = 20; } } - else if (streams == AC_SUPFMT_FLOAT32) { - /* should be exclusive */ + if (streams & AC_SUPFMT_FLOAT32) { formats |= SNDRV_PCM_FMTBIT_FLOAT_LE; - bps = 32; - } else if (streams == AC_SUPFMT_AC3) { + if (!bps) + bps = 32; + } + if (streams == AC_SUPFMT_AC3) { /* should be exclusive */ /* temporary hack: we have still no proper support * for the direct AC3 stream... @@ -3102,7 +3127,7 @@ int snd_hda_check_board_codec_sid_config(struct hda_codec *codec, tbl = q; if (tbl->value >= 0 && tbl->value < num_configs) { -#ifdef CONFIG_SND_DEBUG_DETECT +#ifdef CONFIG_SND_DEBUG_VERBOSE char tmp[10]; const char *model = NULL; if (models) @@ -3655,8 +3680,7 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, end_nid = codec->start_nid + codec->num_nodes; for (nid = codec->start_nid; nid < end_nid; nid++) { unsigned int wid_caps = get_wcaps(codec, nid); - unsigned int wid_type = - (wid_caps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; + unsigned int wid_type = get_wcaps_type(wid_caps); unsigned int def_conf; short assoc, loc; diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 1b75f28ed09..99552fb5f75 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -830,7 +830,8 @@ enum { int snd_hda_bus_new(struct snd_card *card, const struct hda_bus_template *temp, struct hda_bus **busp); int snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr, - int do_init, struct hda_codec **codecp); + struct hda_codec **codecp); +int snd_hda_codec_configure(struct hda_codec *codec); /* * low level functions @@ -938,6 +939,13 @@ static inline void snd_hda_power_down(struct hda_codec *codec) {} #define snd_hda_codec_needs_resume(codec) 1 #endif +#ifdef CONFIG_SND_HDA_PATCH_LOADER +/* + * patch firmware + */ +int snd_hda_load_patch(struct hda_bus *bus, const char *patch); +#endif + /* * Codec modularization */ diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 1d5797a9668..b36f6c5a92d 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -121,11 +121,17 @@ static int add_new_node(struct hda_codec *codec, struct hda_gspec *spec, hda_nid if (node == NULL) return -ENOMEM; node->nid = nid; - nconns = snd_hda_get_connections(codec, nid, conn_list, - HDA_MAX_CONNECTIONS); - if (nconns < 0) { - kfree(node); - return nconns; + node->wid_caps = get_wcaps(codec, nid); + node->type = get_wcaps_type(node->wid_caps); + if (node->wid_caps & AC_WCAP_CONN_LIST) { + nconns = snd_hda_get_connections(codec, nid, conn_list, + HDA_MAX_CONNECTIONS); + if (nconns < 0) { + kfree(node); + return nconns; + } + } else { + nconns = 0; } if (nconns <= ARRAY_SIZE(node->slist)) node->conn_list = node->slist; @@ -140,8 +146,6 @@ static int add_new_node(struct hda_codec *codec, struct hda_gspec *spec, hda_nid } memcpy(node->conn_list, conn_list, nconns * sizeof(hda_nid_t)); node->nconns = nconns; - node->wid_caps = get_wcaps(codec, nid); - node->type = (node->wid_caps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; if (node->type == AC_WID_PIN) { node->pin_caps = snd_hda_query_pin_caps(codec, node->nid); diff --git a/sound/pci/hda/hda_hwdep.c b/sound/pci/hda/hda_hwdep.c index 6812fbe80fa..cc24e6721d7 100644 --- a/sound/pci/hda/hda_hwdep.c +++ b/sound/pci/hda/hda_hwdep.c @@ -24,6 +24,7 @@ #include <linux/compat.h> #include <linux/mutex.h> #include <linux/ctype.h> +#include <linux/firmware.h> #include <sound/core.h> #include "hda_codec.h" #include "hda_local.h" @@ -312,12 +313,8 @@ static ssize_t init_verbs_show(struct device *dev, return len; } -static ssize_t init_verbs_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) +static int parse_init_verbs(struct hda_codec *codec, const char *buf) { - struct snd_hwdep *hwdep = dev_get_drvdata(dev); - struct hda_codec *codec = hwdep->private_data; struct hda_verb *v; int nid, verb, param; @@ -331,6 +328,18 @@ static ssize_t init_verbs_store(struct device *dev, v->nid = nid; v->verb = verb; v->param = param; + return 0; +} + +static ssize_t init_verbs_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct snd_hwdep *hwdep = dev_get_drvdata(dev); + struct hda_codec *codec = hwdep->private_data; + int err = parse_init_verbs(codec, buf); + if (err < 0) + return err; return count; } @@ -376,19 +385,15 @@ static void remove_trail_spaces(char *str) #define MAX_HINTS 1024 -static ssize_t hints_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) +static int parse_hints(struct hda_codec *codec, const char *buf) { - struct snd_hwdep *hwdep = dev_get_drvdata(dev); - struct hda_codec *codec = hwdep->private_data; char *key, *val; struct hda_hint *hint; while (isspace(*buf)) buf++; if (!*buf || *buf == '#' || *buf == '\n') - return count; + return 0; if (*buf == '=') return -EINVAL; key = kstrndup_noeol(buf, 1024); @@ -411,7 +416,7 @@ static ssize_t hints_store(struct device *dev, kfree(hint->key); hint->key = key; hint->val = val; - return count; + return 0; } /* allocate a new hint entry */ if (codec->hints.used >= MAX_HINTS) @@ -424,6 +429,18 @@ static ssize_t hints_store(struct device *dev, } hint->key = key; hint->val = val; + return 0; +} + +static ssize_t hints_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct snd_hwdep *hwdep = dev_get_drvdata(dev); + struct hda_codec *codec = hwdep->private_data; + int err = parse_hints(codec, buf); + if (err < 0) + return err; return count; } @@ -469,20 +486,24 @@ static ssize_t driver_pin_configs_show(struct device *dev, #define MAX_PIN_CONFIGS 32 -static ssize_t user_pin_configs_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) +static int parse_user_pin_configs(struct hda_codec *codec, const char *buf) { - struct snd_hwdep *hwdep = dev_get_drvdata(dev); - struct hda_codec *codec = hwdep->private_data; int nid, cfg; - int err; if (sscanf(buf, "%i %i", &nid, &cfg) != 2) return -EINVAL; if (!nid) return -EINVAL; - err = snd_hda_add_pincfg(codec, &codec->user_pins, nid, cfg); + return snd_hda_add_pincfg(codec, &codec->user_pins, nid, cfg); +} + +static ssize_t user_pin_configs_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct snd_hwdep *hwdep = dev_get_drvdata(dev); + struct hda_codec *codec = hwdep->private_data; + int err = parse_user_pin_configs(codec, buf); if (err < 0) return err; return count; @@ -553,3 +574,180 @@ int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key) EXPORT_SYMBOL_HDA(snd_hda_get_bool_hint); #endif /* CONFIG_SND_HDA_RECONFIG */ + +#ifdef CONFIG_SND_HDA_PATCH_LOADER + +/* parser mode */ +enum { + LINE_MODE_NONE, + LINE_MODE_CODEC, + LINE_MODE_MODEL, + LINE_MODE_PINCFG, + LINE_MODE_VERB, + LINE_MODE_HINT, + NUM_LINE_MODES, +}; + +static inline int strmatch(const char *a, const char *b) +{ + return strnicmp(a, b, strlen(b)) == 0; +} + +/* parse the contents after the line "[codec]" + * accept only the line with three numbers, and assign the current codec + */ +static void parse_codec_mode(char *buf, struct hda_bus *bus, + struct hda_codec **codecp) +{ + unsigned int vendorid, subid, caddr; + struct hda_codec *codec; + + *codecp = NULL; + if (sscanf(buf, "%i %i %i", &vendorid, &subid, &caddr) == 3) { + list_for_each_entry(codec, &bus->codec_list, list) { + if (codec->addr == caddr) { + *codecp = codec; + break; + } + } + } +} + +/* parse the contents after the other command tags, [pincfg], [verb], + * [hint] and [model] + * just pass to the sysfs helper (only when any codec was specified) + */ +static void parse_pincfg_mode(char *buf, struct hda_bus *bus, + struct hda_codec **codecp) +{ + if (!*codecp) + return; + parse_user_pin_configs(*codecp, buf); +} + +static void parse_verb_mode(char *buf, struct hda_bus *bus, + struct hda_codec **codecp) +{ + if (!*codecp) + return; + parse_init_verbs(*codecp, buf); +} + +static void parse_hint_mode(char *buf, struct hda_bus *bus, + struct hda_codec **codecp) +{ + if (!*codecp) + return; + parse_hints(*codecp, buf); +} + +static void parse_model_mode(char *buf, struct hda_bus *bus, + struct hda_codec **codecp) +{ + if (!*codecp) + return; + kfree((*codecp)->modelname); + (*codecp)->modelname = kstrdup(buf, GFP_KERNEL); +} + +struct hda_patch_item { + const char *tag; + void (*parser)(char *buf, struct hda_bus *bus, struct hda_codec **retc); +}; + +static struct hda_patch_item patch_items[NUM_LINE_MODES] = { + [LINE_MODE_CODEC] = { "[codec]", parse_codec_mode }, + [LINE_MODE_MODEL] = { "[model]", parse_model_mode }, + [LINE_MODE_VERB] = { "[verb]", parse_verb_mode }, + [LINE_MODE_PINCFG] = { "[pincfg]", parse_pincfg_mode }, + [LINE_MODE_HINT] = { "[hint]", parse_hint_mode }, +}; + +/* check the line starting with '[' -- change the parser mode accodingly */ +static int parse_line_mode(char *buf, struct hda_bus *bus) +{ + int i; + for (i = 0; i < ARRAY_SIZE(patch_items); i++) { + if (!patch_items[i].tag) + continue; + if (strmatch(buf, patch_items[i].tag)) + return i; + } + return LINE_MODE_NONE; +} + +/* copy one line from the buffer in fw, and update the fields in fw + * return zero if it reaches to the end of the buffer, or non-zero + * if successfully copied a line + * + * the spaces at the beginning and the end of the line are stripped + */ +static int get_line_from_fw(char *buf, int size, struct firmware *fw) +{ + int len; + const char *p = fw->data; + while (isspace(*p) && fw->size) { + p++; + fw->size--; + } + if (!fw->size) + return 0; + if (size < fw->size) + size = fw->size; + + for (len = 0; len < fw->size; len++) { + if (!*p) + break; + if (*p == '\n') { + p++; + len++; + break; + } + if (len < size) + *buf++ = *p++; + } + *buf = 0; + fw->size -= len; + fw->data = p; + remove_trail_spaces(buf); + return 1; +} + +/* + * load a "patch" firmware file and parse it + */ +int snd_hda_load_patch(struct hda_bus *bus, const char *patch) +{ + int err; + const struct firmware *fw; + struct firmware tmp; + char buf[128]; + struct hda_codec *codec; + int line_mode; + struct device *dev = bus->card->dev; + + if (snd_BUG_ON(!dev)) + return -ENODEV; + err = request_firmware(&fw, patch, dev); + if (err < 0) { + printk(KERN_ERR "hda-codec: Cannot load the patch '%s'\n", + patch); + return err; + } + + tmp = *fw; + line_mode = LINE_MODE_NONE; + codec = NULL; + while (get_line_from_fw(buf, sizeof(buf) - 1, &tmp)) { + if (!*buf || *buf == '#' || *buf == '\n') + continue; + if (*buf == '[') + line_mode = parse_line_mode(buf, bus); + else if (patch_items[line_mode].parser) + patch_items[line_mode].parser(buf, bus, &codec); + } + release_firmware(fw); + return 0; +} +EXPORT_SYMBOL_HDA(snd_hda_load_patch); +#endif /* CONFIG_SND_HDA_PATCH_LOADER */ diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 175f07a381b..20a66f85f0a 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -61,6 +61,9 @@ static int probe_mask[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1}; static int probe_only[SNDRV_CARDS]; static int single_cmd; static int enable_msi; +#ifdef CONFIG_SND_HDA_PATCH_LOADER +static char *patch[SNDRV_CARDS]; +#endif module_param_array(index, int, NULL, 0444); MODULE_PARM_DESC(index, "Index value for Intel HD audio interface."); @@ -84,6 +87,10 @@ MODULE_PARM_DESC(single_cmd, "Use single command to communicate with codecs " "(for debugging only)."); module_param(enable_msi, int, 0444); MODULE_PARM_DESC(enable_msi, "Enable Message Signaled Interrupt (MSI)"); +#ifdef CONFIG_SND_HDA_PATCH_LOADER +module_param_array(patch, charp, NULL, 0444); +MODULE_PARM_DESC(patch, "Patch file for Intel HD audio interface."); +#endif #ifdef CONFIG_SND_HDA_POWER_SAVE static int power_save = CONFIG_SND_HDA_POWER_SAVE_DEFAULT; @@ -1331,8 +1338,7 @@ static unsigned int azx_max_codecs[AZX_NUM_DRIVERS] __devinitdata = { [AZX_DRIVER_TERA] = 1, }; -static int __devinit azx_codec_create(struct azx *chip, const char *model, - int no_init) +static int __devinit azx_codec_create(struct azx *chip, const char *model) { struct hda_bus_template bus_temp; int c, codecs, err; @@ -1391,7 +1397,7 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model, for (c = 0; c < max_slots; c++) { if ((chip->codec_mask & (1 << c)) & chip->codec_probe_mask) { struct hda_codec *codec; - err = snd_hda_codec_new(chip->bus, c, !no_init, &codec); + err = snd_hda_codec_new(chip->bus, c, &codec); |