Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 157775
b: refs/heads/master
c: 4ea6fbc
h: refs/heads/master
i:
  157773: a05aa9c
  157771: cc4caa0
  157767: a624fc6
  157759: f2a887a
v: v3
  • Loading branch information
Takashi Iwai committed Jun 24, 2009
1 parent 660b4d5 commit e21e13f
Show file tree
Hide file tree
Showing 5 changed files with 258 additions and 22 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: a1e21c9078fb8005e5accb921696ec9e2f38176e
refs/heads/master: 4ea6fbc8eb23c3ae5fd2fb55a340ab85c8649bce
14 changes: 14 additions & 0 deletions trunk/sound/pci/hda/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 7 additions & 0 deletions trunk/sound/pci/hda/hda_codec.h
Original file line number Diff line number Diff line change
Expand Up @@ -939,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
*/
Expand Down
236 changes: 217 additions & 19 deletions trunk/sound/pci/hda/hda_hwdep.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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;

Expand All @@ -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;
}

Expand Down Expand Up @@ -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);
Expand All @@ -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)
Expand All @@ -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;
}

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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 */
21 changes: 19 additions & 2 deletions trunk/sound/pci/hda/hda_intel.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.");
Expand All @@ -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;
Expand Down Expand Up @@ -2468,6 +2475,9 @@ static int __devinit azx_probe(struct pci_dev *pci,
return err;
}

/* set this here since it's referred in snd_hda_load_patch() */
snd_card_set_dev(card, &pci->dev);

err = azx_create(card, pci, dev, pci_id->driver_data, &chip);
if (err < 0)
goto out_free;
Expand All @@ -2477,6 +2487,15 @@ static int __devinit azx_probe(struct pci_dev *pci,
err = azx_codec_create(chip, model[dev]);
if (err < 0)
goto out_free;
#ifdef CONFIG_SND_HDA_PATCH_LOADER
if (patch[dev]) {
snd_printk(KERN_ERR SFX "Applying patch firmware '%s'\n",
patch[dev]);
err = snd_hda_load_patch(chip->bus, patch[dev]);
if (err < 0)
goto out_free;
}
#endif
if (!probe_only[dev]) {
err = azx_codec_configure(chip);
if (err < 0)
Expand All @@ -2493,8 +2512,6 @@ static int __devinit azx_probe(struct pci_dev *pci,
if (err < 0)
goto out_free;

snd_card_set_dev(card, &pci->dev);

err = snd_card_register(card);
if (err < 0)
goto out_free;
Expand Down

0 comments on commit e21e13f

Please sign in to comment.