aboutsummaryrefslogtreecommitdiffstats
path: root/sound/pci/hda/hda_hwdep.c
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2009-06-17 03:52:54 -0400
committerTakashi Iwai <tiwai@suse.de>2009-06-24 05:53:43 -0400
commit4ea6fbc8eb23c3ae5fd2fb55a340ab85c8649bce (patch)
tree8e13b14c449c13047d92f7bed84d4e83eebb96ce /sound/pci/hda/hda_hwdep.c
parenta1e21c9078fb8005e5accb921696ec9e2f38176e (diff)
ALSA: hda - Add patch module option
Added the patch module option to apply a "patch" as a firmware to modify pin configurations or give additional hints to the driver before actually initializing and configuring the codec. This can be used as a workaround when the BIOS doesn't give sufficient information or give wrong information that doesn't match with the real hardware setup, until it's fixed statically in the driver via a quirk. Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/pci/hda/hda_hwdep.c')
-rw-r--r--sound/pci/hda/hda_hwdep.c236
1 files changed, 217 insertions, 19 deletions
diff --git a/sound/pci/hda/hda_hwdep.c b/sound/pci/hda/hda_hwdep.c
index 6812fbe80fa4..cc24e6721d74 100644
--- a/sound/pci/hda/hda_hwdep.c
+++ b/sound/pci/hda/hda_hwdep.c
@@ -24,6 +24,7 @@
24#include <linux/compat.h> 24#include <linux/compat.h>
25#include <linux/mutex.h> 25#include <linux/mutex.h>
26#include <linux/ctype.h> 26#include <linux/ctype.h>
27#include <linux/firmware.h>
27#include <sound/core.h> 28#include <sound/core.h>
28#include "hda_codec.h" 29#include "hda_codec.h"
29#include "hda_local.h" 30#include "hda_local.h"
@@ -312,12 +313,8 @@ static ssize_t init_verbs_show(struct device *dev,
312 return len; 313 return len;
313} 314}
314 315
315static ssize_t init_verbs_store(struct device *dev, 316static int parse_init_verbs(struct hda_codec *codec, const char *buf)
316 struct device_attribute *attr,
317 const char *buf, size_t count)
318{ 317{
319 struct snd_hwdep *hwdep = dev_get_drvdata(dev);
320 struct hda_codec *codec = hwdep->private_data;
321 struct hda_verb *v; 318 struct hda_verb *v;
322 int nid, verb, param; 319 int nid, verb, param;
323 320
@@ -331,6 +328,18 @@ static ssize_t init_verbs_store(struct device *dev,
331 v->nid = nid; 328 v->nid = nid;
332 v->verb = verb; 329 v->verb = verb;
333 v->param = param; 330 v->param = param;
331 return 0;
332}
333
334static ssize_t init_verbs_store(struct device *dev,
335 struct device_attribute *attr,
336 const char *buf, size_t count)
337{
338 struct snd_hwdep *hwdep = dev_get_drvdata(dev);
339 struct hda_codec *codec = hwdep->private_data;
340 int err = parse_init_verbs(codec, buf);
341 if (err < 0)
342 return err;
334 return count; 343 return count;
335} 344}
336 345
@@ -376,19 +385,15 @@ static void remove_trail_spaces(char *str)
376 385
377#define MAX_HINTS 1024 386#define MAX_HINTS 1024
378 387
379static ssize_t hints_store(struct device *dev, 388static int parse_hints(struct hda_codec *codec, const char *buf)
380 struct device_attribute *attr,
381 const char *buf, size_t count)
382{ 389{
383 struct snd_hwdep *hwdep = dev_get_drvdata(dev);
384 struct hda_codec *codec = hwdep->private_data;
385 char *key, *val; 390 char *key, *val;
386 struct hda_hint *hint; 391 struct hda_hint *hint;
387 392
388 while (isspace(*buf)) 393 while (isspace(*buf))
389 buf++; 394 buf++;
390 if (!*buf || *buf == '#' || *buf == '\n') 395 if (!*buf || *buf == '#' || *buf == '\n')
391 return count; 396 return 0;
392 if (*buf == '=') 397 if (*buf == '=')
393 return -EINVAL; 398 return -EINVAL;
394 key = kstrndup_noeol(buf, 1024); 399 key = kstrndup_noeol(buf, 1024);
@@ -411,7 +416,7 @@ static ssize_t hints_store(struct device *dev,
411 kfree(hint->key); 416 kfree(hint->key);
412 hint->key = key; 417 hint->key = key;
413 hint->val = val; 418 hint->val = val;
414 return count; 419 return 0;
415 } 420 }
416 /* allocate a new hint entry */ 421 /* allocate a new hint entry */
417 if (codec->hints.used >= MAX_HINTS) 422 if (codec->hints.used >= MAX_HINTS)
@@ -424,6 +429,18 @@ static ssize_t hints_store(struct device *dev,
424 } 429 }
425 hint->key = key; 430 hint->key = key;
426 hint->val = val; 431 hint->val = val;
432 return 0;
433}
434
435static ssize_t hints_store(struct device *dev,
436 struct device_attribute *attr,
437 const char *buf, size_t count)
438{
439 struct snd_hwdep *hwdep = dev_get_drvdata(dev);
440 struct hda_codec *codec = hwdep->private_data;
441 int err = parse_hints(codec, buf);
442 if (err < 0)
443 return err;
427 return count; 444 return count;
428} 445}
429 446
@@ -469,20 +486,24 @@ static ssize_t driver_pin_configs_show(struct device *dev,
469 486
470#define MAX_PIN_CONFIGS 32 487#define MAX_PIN_CONFIGS 32
471 488
472static ssize_t user_pin_configs_store(struct device *dev, 489static int parse_user_pin_configs(struct hda_codec *codec, const char *buf)
473 struct device_attribute *attr,
474 const char *buf, size_t count)
475{ 490{
476 struct snd_hwdep *hwdep = dev_get_drvdata(dev);
477 struct hda_codec *codec = hwdep->private_data;
478 int nid, cfg; 491 int nid, cfg;
479 int err;
480 492
481 if (sscanf(buf, "%i %i", &nid, &cfg) != 2) 493 if (sscanf(buf, "%i %i", &nid, &cfg) != 2)
482 return -EINVAL; 494 return -EINVAL;
483 if (!nid) 495 if (!nid)
484 return -EINVAL; 496 return -EINVAL;
485 err = snd_hda_add_pincfg(codec, &codec->user_pins, nid, cfg); 497 return snd_hda_add_pincfg(codec, &codec->user_pins, nid, cfg);
498}
499
500static ssize_t user_pin_configs_store(struct device *dev,
501 struct device_attribute *attr,
502 const char *buf, size_t count)
503{
504 struct snd_hwdep *hwdep = dev_get_drvdata(dev);
505 struct hda_codec *codec = hwdep->private_data;
506 int err = parse_user_pin_configs(codec, buf);
486 if (err < 0) 507 if (err < 0)
487 return err; 508 return err;
488 return count; 509 return count;
@@ -553,3 +574,180 @@ int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key)
553EXPORT_SYMBOL_HDA(snd_hda_get_bool_hint); 574EXPORT_SYMBOL_HDA(snd_hda_get_bool_hint);
554 575
555#endif /* CONFIG_SND_HDA_RECONFIG */ 576#endif /* CONFIG_SND_HDA_RECONFIG */
577
578#ifdef CONFIG_SND_HDA_PATCH_LOADER
579
580/* parser mode */
581enum {
582 LINE_MODE_NONE,
583 LINE_MODE_CODEC,
584 LINE_MODE_MODEL,
585 LINE_MODE_PINCFG,
586 LINE_MODE_VERB,
587 LINE_MODE_HINT,
588 NUM_LINE_MODES,
589};
590
591static inline int strmatch(const char *a, const char *b)
592{
593 return strnicmp(a, b, strlen(b)) == 0;
594}
595
596/* parse the contents after the line "[codec]"
597 * accept only the line with three numbers, and assign the current codec
598 */
599static void parse_codec_mode(char *buf, struct hda_bus *bus,
600 struct hda_codec **codecp)
601{
602 unsigned int vendorid, subid, caddr;
603 struct hda_codec *codec;
604
605 *codecp = NULL;
606 if (sscanf(buf, "%i %i %i", &vendorid, &subid, &caddr) == 3) {
607 list_for_each_entry(codec, &bus->codec_list, list) {
608 if (codec->addr == caddr) {
609 *codecp = codec;
610 break;
611 }
612 }
613 }
614}
615
616/* parse the contents after the other command tags, [pincfg], [verb],
617 * [hint] and [model]
618 * just pass to the sysfs helper (only when any codec was specified)
619 */
620static void parse_pincfg_mode(char *buf, struct hda_bus *bus,
621 struct hda_codec **codecp)
622{
623 if (!*codecp)
624 return;
625 parse_user_pin_configs(*codecp, buf);
626}
627
628static void parse_verb_mode(char *buf, struct hda_bus *bus,
629 struct hda_codec **codecp)
630{
631 if (!*codecp)
632 return;
633 parse_init_verbs(*codecp, buf);
634}
635
636static void parse_hint_mode(char *buf, struct hda_bus *bus,
637 struct hda_codec **codecp)
638{
639 if (!*codecp)
640 return;
641 parse_hints(*codecp, buf);
642}
643
644static void parse_model_mode(char *buf, struct hda_bus *bus,
645 struct hda_codec **codecp)
646{
647 if (!*codecp)
648 return;
649 kfree((*codecp)->modelname);
650 (*codecp)->modelname = kstrdup(buf, GFP_KERNEL);
651}
652
653struct hda_patch_item {
654 const char *tag;
655 void (*parser)(char *buf, struct hda_bus *bus, struct hda_codec **retc);
656};
657
658static struct hda_patch_item patch_items[NUM_LINE_MODES] = {
659 [LINE_MODE_CODEC] = { "[codec]", parse_codec_mode },
660 [LINE_MODE_MODEL] = { "[model]", parse_model_mode },
661 [LINE_MODE_VERB] = { "[verb]", parse_verb_mode },
662 [LINE_MODE_PINCFG] = { "[pincfg]", parse_pincfg_mode },
663 [LINE_MODE_HINT] = { "[hint]", parse_hint_mode },
664};
665
666/* check the line starting with '[' -- change the parser mode accodingly */
667static int parse_line_mode(char *buf, struct hda_bus *bus)
668{
669 int i;
670 for (i = 0; i < ARRAY_SIZE(patch_items); i++) {
671 if (!patch_items[i].tag)
672 continue;
673 if (strmatch(buf, patch_items[i].tag))
674 return i;
675 }
676 return LINE_MODE_NONE;
677}
678
679/* copy one line from the buffer in fw, and update the fields in fw
680 * return zero if it reaches to the end of the buffer, or non-zero
681 * if successfully copied a line
682 *
683 * the spaces at the beginning and the end of the line are stripped
684 */
685static int get_line_from_fw(char *buf, int size, struct firmware *fw)
686{
687 int len;
688 const char *p = fw->data;
689 while (isspace(*p) && fw->size) {
690 p++;
691 fw->size--;
692 }
693 if (!fw->size)
694 return 0;
695 if (size < fw->size)
696 size = fw->size;
697
698 for (len = 0; len < fw->size; len++) {
699 if (!*p)
700 break;
701 if (*p == '\n') {
702 p++;
703 len++;
704 break;
705 }
706 if (len < size)
707 *buf++ = *p++;
708 }
709 *buf = 0;
710 fw->size -= len;
711 fw->data = p;
712 remove_trail_spaces(buf);
713 return 1;
714}
715
716/*
717 * load a "patch" firmware file and parse it
718 */
719int snd_hda_load_patch(struct hda_bus *bus, const char *patch)
720{
721 int err;
722 const struct firmware *fw;
723 struct firmware tmp;
724 char buf[128];
725 struct hda_codec *codec;
726 int line_mode;
727 struct device *dev = bus->card->dev;
728
729 if (snd_BUG_ON(!dev))
730 return -ENODEV;
731 err = request_firmware(&fw, patch, dev);
732 if (err < 0) {
733 printk(KERN_ERR "hda-codec: Cannot load the patch '%s'\n",
734 patch);
735 return err;
736 }
737
738 tmp = *fw;
739 line_mode = LINE_MODE_NONE;
740 codec = NULL;
741 while (get_line_from_fw(buf, sizeof(buf) - 1, &tmp)) {
742 if (!*buf || *buf == '#' || *buf == '\n')
743 continue;
744 if (*buf == '[')
745 line_mode = parse_line_mode(buf, bus);
746 else if (patch_items[line_mode].parser)
747 patch_items[line_mode].parser(buf, bus, &codec);
748 }
749 release_firmware(fw);
750 return 0;
751}
752EXPORT_SYMBOL_HDA(snd_hda_load_patch);
753#endif /* CONFIG_SND_HDA_PATCH_LOADER */