mirror of
git://projects.qi-hardware.com/openwrt-xburst.git
synced 2024-12-27 05:38:38 +02:00
ba2e2fa7be
git-svn-id: svn://svn.openwrt.org/openwrt/trunk@14454 3c298f89-4303-0410-b956-a3cf2f4a3e73
295 lines
9.5 KiB
Diff
295 lines
9.5 KiB
Diff
--- a/sound/pci/ac97/ac97_codec.c
|
|
+++ b/sound/pci/ac97/ac97_codec.c
|
|
@@ -158,7 +158,7 @@ static const struct ac97_codec_id snd_ac
|
|
{ 0x4e534300, 0xffffffff, "LM4540,43,45,46,48", NULL, NULL }, // only guess --jk
|
|
{ 0x4e534331, 0xffffffff, "LM4549", NULL, NULL },
|
|
{ 0x4e534350, 0xffffffff, "LM4550", patch_lm4550, NULL }, // volume wrap fix
|
|
-{ 0x50534304, 0xffffffff, "UCB1400", patch_ucb1400, NULL },
|
|
+{ 0x50534304, 0xffffffff, "UCB1400", patch_ucb1400, NULL, AC97_HAS_NO_STD_PCM },
|
|
{ 0x53494c20, 0xffffffe0, "Si3036,8", mpatch_si3036, mpatch_si3036, AC97_MODEM_PATCH },
|
|
{ 0x54524102, 0xffffffff, "TR28022", NULL, NULL },
|
|
{ 0x54524106, 0xffffffff, "TR28026", NULL, NULL },
|
|
--- a/sound/pci/ac97/ac97_patch.c
|
|
+++ b/sound/pci/ac97/ac97_patch.c
|
|
@@ -29,6 +29,10 @@
|
|
#include <linux/slab.h>
|
|
#include <linux/mutex.h>
|
|
|
|
+#include <linux/proc_fs.h>
|
|
+#include <linux/string.h>
|
|
+#include <linux/ctype.h>
|
|
+
|
|
#include <sound/core.h>
|
|
#include <sound/pcm.h>
|
|
#include <sound/control.h>
|
|
@@ -406,6 +410,227 @@ int patch_yamaha_ymf753(struct snd_ac97
|
|
}
|
|
|
|
/*
|
|
+ * UCB1400 codec
|
|
+ */
|
|
+
|
|
+#define AC97_UCB1400_FCSR1 0x6a
|
|
+#define AC97_UCB1400_FCSR2 0x6c
|
|
+
|
|
+static const struct snd_kcontrol_new ucb1400_snd_ac97_controls[] = {
|
|
+ AC97_SINGLE("Tone Control - Bass", AC97_UCB1400_FCSR1, 11, 4, 0),
|
|
+ AC97_SINGLE("Tone Control - Treble", AC97_UCB1400_FCSR1, 9, 2, 0),
|
|
+ AC97_SINGLE("Headphone Playback Switch", AC97_UCB1400_FCSR1, 6, 1, 0),
|
|
+ AC97_SINGLE("De-emphasis", AC97_UCB1400_FCSR1, 5, 1, 0),
|
|
+ AC97_SINGLE("DC Filter", AC97_UCB1400_FCSR1, 4, 1, 0),
|
|
+ AC97_SINGLE("Hi-pass Filter", AC97_UCB1400_FCSR1, 3, 1, 0),
|
|
+ AC97_SINGLE("ADC Filter", AC97_UCB1400_FCSR2, 12, 1, 0),
|
|
+};
|
|
+
|
|
+#define NUM_GPIO_LINES 10
|
|
+
|
|
+static struct proc_dir_entry *proc_gpio_parent;
|
|
+static struct proc_dir_entry *proc_gpios[NUM_GPIO_LINES];
|
|
+
|
|
+typedef struct
|
|
+{
|
|
+ int gpio;
|
|
+ char name[32];
|
|
+ struct snd_ac97 *ac97;
|
|
+} gpio_summary_type;
|
|
+
|
|
+static gpio_summary_type gpio_summaries[NUM_GPIO_LINES] =
|
|
+{
|
|
+ { 0, "UCB1400-0-0" },
|
|
+ { 1, "UCB1400-0-1" },
|
|
+ { 2, "UCB1400-0-2" },
|
|
+ { 3, "UCB1400-0-3" },
|
|
+ { 4, "UCB1400-0-4" },
|
|
+ { 5, "UCB1400-0-5" },
|
|
+ { 6, "UCB1400-0-6" },
|
|
+ { 7, "UCB1400-0-7" },
|
|
+ { 8, "UCB1400-0-8" },
|
|
+ { 9, "UCB1400-0-9" }
|
|
+};
|
|
+
|
|
+
|
|
+static int proc_ucb1400_ac97_gpio_write(struct file *file, const char __user *buf,
|
|
+ unsigned long count, void *data)
|
|
+{
|
|
+ char *cur, lbuf[count + 1];
|
|
+ gpio_summary_type *summary = data;
|
|
+ u32 direction_is_out, operation_is_set;
|
|
+ int i = summary->gpio;
|
|
+ u16 dir, value;
|
|
+
|
|
+ if (!capable(CAP_SYS_ADMIN))
|
|
+ return -EACCES;
|
|
+
|
|
+ memset(lbuf, 0, count + 1);
|
|
+
|
|
+ if (copy_from_user(lbuf, buf, count))
|
|
+ return -EFAULT;
|
|
+
|
|
+ cur = lbuf;
|
|
+
|
|
+ // Get current values
|
|
+ direction_is_out = !!(snd_ac97_read(summary->ac97, 0x5c) & (0x0001 << i));
|
|
+ operation_is_set = !!(snd_ac97_read(summary->ac97, 0x5a) & (0x0001 << i));
|
|
+ while(1)
|
|
+ {
|
|
+ // We accept options: {GPIO|AF1|AF2|AF3}, {set|clear}, {in|out}
|
|
+ // Anything else is an error
|
|
+ while(cur[0] && (isspace(cur[0]) || ispunct(cur[0]))) cur = &(cur[1]);
|
|
+
|
|
+ if('\0' == cur[0]) break;
|
|
+
|
|
+ // Ok, so now we're pointing at the start of something
|
|
+ switch(cur[0])
|
|
+ {
|
|
+ case 'G':
|
|
+ // Check that next is "PIO" -- '\0' will cause safe short-circuit if end of buf
|
|
+ if(!(cur[1] == 'P' && cur[2] == 'I' && cur[3] == 'O')) goto parse_error;
|
|
+ cur = &(cur[4]);
|
|
+ break;
|
|
+ case 's':
|
|
+ if(!(cur[1] == 'e' && cur[2] == 't')) goto parse_error;
|
|
+ operation_is_set = 1;
|
|
+ cur = &(cur[3]);
|
|
+ break;
|
|
+ case 'c':
|
|
+ if(!(cur[1] == 'l' && cur[2] == 'e' && cur[3] == 'a' && cur[4] == 'r')) goto
|
|
+parse_error;
|
|
+ operation_is_set = 0;
|
|
+ cur = &(cur[5]);
|
|
+ break;
|
|
+ case 'i':
|
|
+ if(!(cur[1] == 'n')) goto parse_error;
|
|
+ direction_is_out = 0;
|
|
+ cur = &(cur[2]);
|
|
+ break;
|
|
+ case 'o':
|
|
+ if(!(cur[1] == 'u' && cur[2] == 't')) goto parse_error;
|
|
+ direction_is_out = 1;
|
|
+ cur = &(cur[3]);
|
|
+ break;
|
|
+ default: goto parse_error;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ // set/get value
|
|
+ dir = snd_ac97_read(summary->ac97, 0x5c);
|
|
+ value = snd_ac97_read(summary->ac97, 0x5a);
|
|
+ if (direction_is_out)
|
|
+ {
|
|
+ dir |= 0x0001 << i;
|
|
+ if (operation_is_set)
|
|
+ {
|
|
+ value |= 0x0001 << i;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ value &= ~(0x0001 << i);
|
|
+ }
|
|
+
|
|
+ snd_ac97_write(summary->ac97, 0x5c, dir);
|
|
+ snd_ac97_write(summary->ac97, 0x5a, value);
|
|
+ }
|
|
+ else // direction in
|
|
+ {
|
|
+ dir &= ~(0x0001 << i);
|
|
+ snd_ac97_write(summary->ac97, 0x5c, dir);
|
|
+ operation_is_set = snd_ac97_read(summary->ac97, 0x5a) & ~(0x0001 << i);
|
|
+ }
|
|
+
|
|
+#ifdef CONFIG_PROC_GPIO_DEBUG
|
|
+ printk(KERN_INFO "Set (%s,%s,%s) via /proc/gpio/%s\n",
|
|
+ "GPIO",
|
|
+ direction_is_out ? "out" : "in",
|
|
+ operation_is_set ? "set" : "clear",
|
|
+ summary->name);
|
|
+#endif
|
|
+
|
|
+ return count;
|
|
+
|
|
+parse_error:
|
|
+ printk(KERN_CRIT "Parse error: Expect \"GPIO|[set|clear]|[in|out] ...\"\n");
|
|
+ return -EINVAL;
|
|
+}
|
|
+
|
|
+static int proc_ucb1400_ac97_gpio_read(char *page, char **start, off_t off,
|
|
+ int count, int *eof, void *data)
|
|
+{
|
|
+ char *p = page;
|
|
+ gpio_summary_type *summary = data;
|
|
+ int len, i; /*, af;*/
|
|
+ i = summary->gpio;
|
|
+
|
|
+ p += sprintf(p, "%d\t%s\t%s\t%s\n", i,
|
|
+ "GPIO",
|
|
+ (snd_ac97_read(summary->ac97, 0x5c) & (0x0001 << i)) ? "out" : "in",
|
|
+ (snd_ac97_read(summary->ac97, 0x5a) & (0x0001 << i)) ? "set" : "clear");
|
|
+
|
|
+ len = (p - page) - off;
|
|
+
|
|
+ if(len < 0)
|
|
+ {
|
|
+ len = 0;
|
|
+ }
|
|
+
|
|
+ *eof = (len <= count) ? 1 : 0;
|
|
+ *start = page + off;
|
|
+
|
|
+ return len;
|
|
+}
|
|
+
|
|
+int patch_ucb1400(struct snd_ac97 * ac97)
|
|
+{
|
|
+ int err, i;
|
|
+
|
|
+ proc_gpio_parent = proc_mkdir("gpio", NULL);
|
|
+ if(!proc_gpio_parent) return 0;
|
|
+
|
|
+ for(i=0; i < NUM_GPIO_LINES; i++)
|
|
+ {
|
|
+ proc_gpios[i] = create_proc_entry(gpio_summaries[i].name, 0644, proc_gpio_parent);
|
|
+ if(proc_gpios[i])
|
|
+ {
|
|
+ gpio_summaries[i].ac97 = ac97;
|
|
+ proc_gpios[i]->data = &gpio_summaries[i];
|
|
+ proc_gpios[i]->read_proc = proc_ucb1400_ac97_gpio_read;
|
|
+ proc_gpios[i]->write_proc = proc_ucb1400_ac97_gpio_write;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ for(i = 0; i < ARRAY_SIZE(ucb1400_snd_ac97_controls); i++) {
|
|
+ if((err = snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&ucb1400_snd_ac97_controls[i], ac97))) < 0)
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ snd_ac97_write_cache(ac97, AC97_UCB1400_FCSR1,
|
|
+ (0 << 11) | // 0 base boost
|
|
+ (0 << 9) | // 0 treble boost
|
|
+ (0 << 7) | // Mode = flat
|
|
+ (1 << 6) | // Headphones enable
|
|
+ (0 << 5) | // De-emphasis disabled
|
|
+ (1 << 4) | // DC filter enabled
|
|
+ (1 << 3) | // Hi-pass filter enabled
|
|
+ (0 << 2) | // disable interrupt signalling via GPIO_INT
|
|
+ (1 << 0) // clear ADC overflow status if set
|
|
+ );
|
|
+
|
|
+ snd_ac97_write_cache(ac97, AC97_UCB1400_FCSR2,
|
|
+ (0 << 15) | // must be 0
|
|
+ (0 << 13) | // must be 0
|
|
+ (1 << 12) | // ADC filter enabled
|
|
+ (0 << 10) | // must be 0
|
|
+ (0 << 4) | // Smart low power mode on neither Codec nor PLL
|
|
+ (0 << 0) // must be 0
|
|
+ );
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
* May 2, 2003 Liam Girdwood <liam.girdwood@wolfsonmicro.com>
|
|
* removed broken wolfson00 patch.
|
|
* added support for WM9705,WM9708,WM9709,WM9710,WM9711,WM9712 and WM9717.
|
|
@@ -3408,41 +3633,3 @@ int patch_lm4550(struct snd_ac97 *ac97)
|
|
ac97->res_table = lm4550_restbl;
|
|
return 0;
|
|
}
|
|
-
|
|
-/*
|
|
- * UCB1400 codec (http://www.semiconductors.philips.com/acrobat_download/datasheets/UCB1400-02.pdf)
|
|
- */
|
|
-static const struct snd_kcontrol_new snd_ac97_controls_ucb1400[] = {
|
|
-/* enable/disable headphone driver which allows direct connection to
|
|
- stereo headphone without the use of external DC blocking
|
|
- capacitors */
|
|
-AC97_SINGLE("Headphone Driver", 0x6a, 6, 1, 0),
|
|
-/* Filter used to compensate the DC offset is added in the ADC to remove idle
|
|
- tones from the audio band. */
|
|
-AC97_SINGLE("DC Filter", 0x6a, 4, 1, 0),
|
|
-/* Control smart-low-power mode feature. Allows automatic power down
|
|
- of unused blocks in the ADC analog front end and the PLL. */
|
|
-AC97_SINGLE("Smart Low Power Mode", 0x6c, 4, 3, 0),
|
|
-};
|
|
-
|
|
-static int patch_ucb1400_specific(struct snd_ac97 * ac97)
|
|
-{
|
|
- int idx, err;
|
|
- for (idx = 0; idx < ARRAY_SIZE(snd_ac97_controls_ucb1400); idx++)
|
|
- if ((err = snd_ctl_add(ac97->bus->card, snd_ctl_new1(&snd_ac97_controls_ucb1400[idx], ac97))) < 0)
|
|
- return err;
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static struct snd_ac97_build_ops patch_ucb1400_ops = {
|
|
- .build_specific = patch_ucb1400_specific,
|
|
-};
|
|
-
|
|
-int patch_ucb1400(struct snd_ac97 * ac97)
|
|
-{
|
|
- ac97->build_ops = &patch_ucb1400_ops;
|
|
- /* enable headphone driver and smart low power mode by default */
|
|
- snd_ac97_write(ac97, 0x6a, 0x0050);
|
|
- snd_ac97_write(ac97, 0x6c, 0x0030);
|
|
- return 0;
|
|
-}
|