diff options
35 files changed, 7009 insertions, 1 deletions
diff --git a/sound/Kconfig b/sound/Kconfig index b65ee4701f98..e0d791a98452 100644 --- a/sound/Kconfig +++ b/sound/Kconfig | |||
@@ -58,6 +58,8 @@ source "sound/pci/Kconfig" | |||
58 | 58 | ||
59 | source "sound/ppc/Kconfig" | 59 | source "sound/ppc/Kconfig" |
60 | 60 | ||
61 | source "sound/aoa/Kconfig" | ||
62 | |||
61 | source "sound/arm/Kconfig" | 63 | source "sound/arm/Kconfig" |
62 | 64 | ||
63 | source "sound/mips/Kconfig" | 65 | source "sound/mips/Kconfig" |
diff --git a/sound/Makefile b/sound/Makefile index f352bb235968..a682ea30f0c9 100644 --- a/sound/Makefile +++ b/sound/Makefile | |||
@@ -4,7 +4,7 @@ | |||
4 | obj-$(CONFIG_SOUND) += soundcore.o | 4 | obj-$(CONFIG_SOUND) += soundcore.o |
5 | obj-$(CONFIG_SOUND_PRIME) += oss/ | 5 | obj-$(CONFIG_SOUND_PRIME) += oss/ |
6 | obj-$(CONFIG_DMASOUND) += oss/ | 6 | obj-$(CONFIG_DMASOUND) += oss/ |
7 | obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ synth/ usb/ sparc/ parisc/ pcmcia/ mips/ | 7 | obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ synth/ usb/ sparc/ parisc/ pcmcia/ mips/ aoa/ |
8 | 8 | ||
9 | ifeq ($(CONFIG_SND),y) | 9 | ifeq ($(CONFIG_SND),y) |
10 | obj-y += last.o | 10 | obj-y += last.o |
diff --git a/sound/aoa/Kconfig b/sound/aoa/Kconfig new file mode 100644 index 000000000000..b11ccf6dbabd --- /dev/null +++ b/sound/aoa/Kconfig | |||
@@ -0,0 +1,17 @@ | |||
1 | menu "Apple Onboard Audio driver" | ||
2 | depends on SND != n && PPC | ||
3 | |||
4 | config SND_AOA | ||
5 | tristate "Apple Onboard Audio driver" | ||
6 | depends on SOUND && SND_PCM | ||
7 | ---help--- | ||
8 | This option enables the new driver for the various | ||
9 | Apple Onboard Audio components. | ||
10 | |||
11 | source "sound/aoa/fabrics/Kconfig" | ||
12 | |||
13 | source "sound/aoa/codecs/Kconfig" | ||
14 | |||
15 | source "sound/aoa/soundbus/Kconfig" | ||
16 | |||
17 | endmenu | ||
diff --git a/sound/aoa/Makefile b/sound/aoa/Makefile new file mode 100644 index 000000000000..d8de3e7df48d --- /dev/null +++ b/sound/aoa/Makefile | |||
@@ -0,0 +1,4 @@ | |||
1 | obj-$(CONFIG_SND_AOA) += core/ | ||
2 | obj-$(CONFIG_SND_AOA) += codecs/ | ||
3 | obj-$(CONFIG_SND_AOA) += fabrics/ | ||
4 | obj-$(CONFIG_SND_AOA_SOUNDBUS) += soundbus/ | ||
diff --git a/sound/aoa/aoa-gpio.h b/sound/aoa/aoa-gpio.h new file mode 100644 index 000000000000..3a61f3115573 --- /dev/null +++ b/sound/aoa/aoa-gpio.h | |||
@@ -0,0 +1,81 @@ | |||
1 | /* | ||
2 | * Apple Onboard Audio GPIO definitions | ||
3 | * | ||
4 | * Copyright 2006 Johannes Berg <johannes@sipsolutions.net> | ||
5 | * | ||
6 | * GPL v2, can be found in COPYING. | ||
7 | */ | ||
8 | |||
9 | #ifndef __AOA_GPIO_H | ||
10 | #define __AOA_GPIO_H | ||
11 | #include <linux/workqueue.h> | ||
12 | #include <linux/mutex.h> | ||
13 | #include <asm/prom.h> | ||
14 | |||
15 | typedef void (*notify_func_t)(void *data); | ||
16 | |||
17 | enum notify_type { | ||
18 | AOA_NOTIFY_HEADPHONE, | ||
19 | AOA_NOTIFY_LINE_IN, | ||
20 | AOA_NOTIFY_LINE_OUT, | ||
21 | }; | ||
22 | |||
23 | struct gpio_runtime; | ||
24 | struct gpio_methods { | ||
25 | /* for initialisation/de-initialisation of the GPIO layer */ | ||
26 | void (*init)(struct gpio_runtime *rt); | ||
27 | void (*exit)(struct gpio_runtime *rt); | ||
28 | |||
29 | /* turn off headphone, speakers, lineout */ | ||
30 | void (*all_amps_off)(struct gpio_runtime *rt); | ||
31 | /* turn headphone, speakers, lineout back to previous setting */ | ||
32 | void (*all_amps_restore)(struct gpio_runtime *rt); | ||
33 | |||
34 | void (*set_headphone)(struct gpio_runtime *rt, int on); | ||
35 | void (*set_speakers)(struct gpio_runtime *rt, int on); | ||
36 | void (*set_lineout)(struct gpio_runtime *rt, int on); | ||
37 | |||
38 | int (*get_headphone)(struct gpio_runtime *rt); | ||
39 | int (*get_speakers)(struct gpio_runtime *rt); | ||
40 | int (*get_lineout)(struct gpio_runtime *rt); | ||
41 | |||
42 | void (*set_hw_reset)(struct gpio_runtime *rt, int on); | ||
43 | |||
44 | /* use this to be notified of any events. The notification | ||
45 | * function is passed the data, and is called in process | ||
46 | * context by the use of schedule_work. | ||
47 | * The interface for it is that setting a function to NULL | ||
48 | * removes it, and they return 0 if the operation succeeded, | ||
49 | * and -EBUSY if the notification is already assigned by | ||
50 | * someone else. */ | ||
51 | int (*set_notify)(struct gpio_runtime *rt, | ||
52 | enum notify_type type, | ||
53 | notify_func_t notify, | ||
54 | void *data); | ||
55 | /* returns 0 if not plugged in, 1 if plugged in | ||
56 | * or a negative error code */ | ||
57 | int (*get_detect)(struct gpio_runtime *rt, | ||
58 | enum notify_type type); | ||
59 | }; | ||
60 | |||
61 | struct gpio_notification { | ||
62 | notify_func_t notify; | ||
63 | void *data; | ||
64 | void *gpio_private; | ||
65 | struct work_struct work; | ||
66 | struct mutex mutex; | ||
67 | }; | ||
68 | |||
69 | struct gpio_runtime { | ||
70 | /* to be assigned by fabric */ | ||
71 | struct device_node *node; | ||
72 | /* since everyone needs this pointer anyway... */ | ||
73 | struct gpio_methods *methods; | ||
74 | /* to be used by the gpio implementation */ | ||
75 | int implementation_private; | ||
76 | struct gpio_notification headphone_notify; | ||
77 | struct gpio_notification line_in_notify; | ||
78 | struct gpio_notification line_out_notify; | ||
79 | }; | ||
80 | |||
81 | #endif /* __AOA_GPIO_H */ | ||
diff --git a/sound/aoa/aoa.h b/sound/aoa/aoa.h new file mode 100644 index 000000000000..378ef1e9879b --- /dev/null +++ b/sound/aoa/aoa.h | |||
@@ -0,0 +1,131 @@ | |||
1 | /* | ||
2 | * Apple Onboard Audio definitions | ||
3 | * | ||
4 | * Copyright 2006 Johannes Berg <johannes@sipsolutions.net> | ||
5 | * | ||
6 | * GPL v2, can be found in COPYING. | ||
7 | */ | ||
8 | |||
9 | #ifndef __AOA_H | ||
10 | #define __AOA_H | ||
11 | #include <asm/prom.h> | ||
12 | #include <linux/module.h> | ||
13 | /* So apparently there's a reason for requiring driver.h to be included first! */ | ||
14 | #include <sound/driver.h> | ||
15 | #include <sound/core.h> | ||
16 | #include <sound/asound.h> | ||
17 | #include <sound/control.h> | ||
18 | #include "aoa-gpio.h" | ||
19 | #include "soundbus/soundbus.h" | ||
20 | |||
21 | #define MAX_CODEC_NAME_LEN 32 | ||
22 | |||
23 | struct aoa_codec { | ||
24 | char name[MAX_CODEC_NAME_LEN]; | ||
25 | |||
26 | struct module *owner; | ||
27 | |||
28 | /* called when the fabric wants to init this codec. | ||
29 | * Do alsa card manipulations from here. */ | ||
30 | int (*init)(struct aoa_codec *codec); | ||
31 | |||
32 | /* called when the fabric is done with the codec. | ||
33 | * The alsa card will be cleaned up so don't bother. */ | ||
34 | void (*exit)(struct aoa_codec *codec); | ||
35 | |||
36 | /* May be NULL, but can be used by the fabric. | ||
37 | * Refcounting is the codec driver's responsibility */ | ||
38 | struct device_node *node; | ||
39 | |||
40 | /* assigned by fabric before init() is called, points | ||
41 | * to the soundbus device. Cannot be NULL. */ | ||
42 | struct soundbus_dev *soundbus_dev; | ||
43 | |||
44 | /* assigned by the fabric before init() is called, points | ||
45 | * to the fabric's gpio runtime record for the relevant | ||
46 | * device. */ | ||
47 | struct gpio_runtime *gpio; | ||
48 | |||
49 | /* assigned by the fabric before init() is called, contains | ||
50 | * a codec specific bitmask of what outputs and inputs are | ||
51 | * actually connected */ | ||
52 | u32 connected; | ||
53 | |||
54 | /* data the fabric can associate with this structure */ | ||
55 | void *fabric_data; | ||
56 | |||
57 | /* private! */ | ||
58 | struct list_head list; | ||
59 | struct aoa_fabric *fabric; | ||
60 | }; | ||
61 | |||
62 | /* return 0 on success */ | ||
63 | extern int | ||
64 | aoa_codec_register(struct aoa_codec *codec); | ||
65 | extern void | ||
66 | aoa_codec_unregister(struct aoa_codec *codec); | ||
67 | |||
68 | #define MAX_LAYOUT_NAME_LEN 32 | ||
69 | |||
70 | struct aoa_fabric { | ||
71 | char name[MAX_LAYOUT_NAME_LEN]; | ||
72 | |||
73 | struct module *owner; | ||
74 | |||
75 | /* once codecs register, they are passed here after. | ||
76 | * They are of course not initialised, since the | ||
77 | * fabric is responsible for initialising some fields | ||
78 | * in the codec structure! */ | ||
79 | int (*found_codec)(struct aoa_codec *codec); | ||
80 | /* called for each codec when it is removed, | ||
81 | * also in the case that aoa_fabric_unregister | ||
82 | * is called and all codecs are removed | ||
83 | * from this fabric. | ||
84 | * Also called if found_codec returned 0 but | ||
85 | * the codec couldn't initialise. */ | ||
86 | void (*remove_codec)(struct aoa_codec *codec); | ||
87 | /* If found_codec returned 0, and the codec | ||
88 | * could be initialised, this is called. */ | ||
89 | void (*attached_codec)(struct aoa_codec *codec); | ||
90 | }; | ||
91 | |||
92 | /* return 0 on success, -EEXIST if another fabric is | ||
93 | * registered, -EALREADY if the same fabric is registered. | ||
94 | * Passing NULL can be used to test for the presence | ||
95 | * of another fabric, if -EALREADY is returned there is | ||
96 | * no other fabric present. | ||
97 | * In the case that the function returns -EALREADY | ||
98 | * and the fabric passed is not NULL, all codecs | ||
99 | * that are not assigned yet are passed to the fabric | ||
100 | * again for reconsideration. */ | ||
101 | extern int | ||
102 | aoa_fabric_register(struct aoa_fabric *fabric); | ||
103 | |||
104 | /* it is vital to call this when the fabric exits! | ||
105 | * When calling, the remove_codec will be called | ||
106 | * for all codecs, unless it is NULL. */ | ||
107 | extern void | ||
108 | aoa_fabric_unregister(struct aoa_fabric *fabric); | ||
109 | |||
110 | /* if for some reason you want to get rid of a codec | ||
111 | * before the fabric is removed, use this. | ||
112 | * Note that remove_codec is called for it! */ | ||
113 | extern void | ||
114 | aoa_fabric_unlink_codec(struct aoa_codec *codec); | ||
115 | |||
116 | /* alsa help methods */ | ||
117 | struct aoa_card { | ||
118 | struct snd_card *alsa_card; | ||
119 | }; | ||
120 | |||
121 | extern int aoa_snd_device_new(snd_device_type_t type, | ||
122 | void * device_data, struct snd_device_ops * ops); | ||
123 | extern struct snd_card *aoa_get_card(void); | ||
124 | extern int aoa_snd_ctl_add(struct snd_kcontrol* control); | ||
125 | |||
126 | /* GPIO stuff */ | ||
127 | extern struct gpio_methods *pmf_gpio_methods; | ||
128 | extern struct gpio_methods *ftr_gpio_methods; | ||
129 | /* extern struct gpio_methods *map_gpio_methods; */ | ||
130 | |||
131 | #endif /* __AOA_H */ | ||
diff --git a/sound/aoa/codecs/Kconfig b/sound/aoa/codecs/Kconfig new file mode 100644 index 000000000000..90cf58f68630 --- /dev/null +++ b/sound/aoa/codecs/Kconfig | |||
@@ -0,0 +1,32 @@ | |||
1 | config SND_AOA_ONYX | ||
2 | tristate "support Onyx chip" | ||
3 | depends on SND_AOA | ||
4 | ---help--- | ||
5 | This option enables support for the Onyx (pcm3052) | ||
6 | codec chip found in the latest Apple machines | ||
7 | (most of those with digital audio output). | ||
8 | |||
9 | #config SND_AOA_TOPAZ | ||
10 | # tristate "support Topaz chips" | ||
11 | # depends on SND_AOA | ||
12 | # ---help--- | ||
13 | # This option enables support for the Topaz (CS84xx) | ||
14 | # codec chips found in the latest Apple machines, | ||
15 | # these chips do the digital input and output on | ||
16 | # some PowerMacs. | ||
17 | |||
18 | config SND_AOA_TAS | ||
19 | tristate "support TAS chips" | ||
20 | depends on SND_AOA | ||
21 | ---help--- | ||
22 | This option enables support for the tas chips | ||
23 | found in a lot of Apple Machines, especially | ||
24 | iBooks and PowerBooks without digital. | ||
25 | |||
26 | config SND_AOA_TOONIE | ||
27 | tristate "support Toonie chip" | ||
28 | depends on SND_AOA | ||
29 | ---help--- | ||
30 | This option enables support for the toonie codec | ||
31 | found in the Mac Mini. If you have a Mac Mini and | ||
32 | want to hear sound, select this option. | ||
diff --git a/sound/aoa/codecs/Makefile b/sound/aoa/codecs/Makefile new file mode 100644 index 000000000000..31cbe68fd42f --- /dev/null +++ b/sound/aoa/codecs/Makefile | |||
@@ -0,0 +1,3 @@ | |||
1 | obj-$(CONFIG_SND_AOA_ONYX) += snd-aoa-codec-onyx.o | ||
2 | obj-$(CONFIG_SND_AOA_TAS) += snd-aoa-codec-tas.o | ||
3 | obj-$(CONFIG_SND_AOA_TOONIE) += snd-aoa-codec-toonie.o | ||
diff --git a/sound/aoa/codecs/snd-aoa-codec-onyx.c b/sound/aoa/codecs/snd-aoa-codec-onyx.c new file mode 100644 index 000000000000..0b7650788f1f --- /dev/null +++ b/sound/aoa/codecs/snd-aoa-codec-onyx.c | |||
@@ -0,0 +1,1113 @@ | |||
1 | /* | ||
2 | * Apple Onboard Audio driver for Onyx codec | ||
3 | * | ||
4 | * Copyright 2006 Johannes Berg <johannes@sipsolutions.net> | ||
5 | * | ||
6 | * GPL v2, can be found in COPYING. | ||
7 | * | ||
8 | * | ||
9 | * This is a driver for the pcm3052 codec chip (codenamed Onyx) | ||
10 | * that is present in newer Apple hardware (with digital output). | ||
11 | * | ||
12 | * The Onyx codec has the following connections (listed by the bit | ||
13 | * to be used in aoa_codec.connected): | ||
14 | * 0: analog output | ||
15 | * 1: digital output | ||
16 | * 2: line input | ||
17 | * 3: microphone input | ||
18 | * Note that even though I know of no machine that has for example | ||
19 | * the digital output connected but not the analog, I have handled | ||
20 | * all the different cases in the code so that this driver may serve | ||
21 | * as a good example of what to do. | ||
22 | * | ||
23 | * NOTE: This driver assumes that there's at most one chip to be | ||
24 | * used with one alsa card, in form of creating all kinds | ||
25 | * of mixer elements without regard for their existence. | ||
26 | * But snd-aoa assumes that there's at most one card, so | ||
27 | * this means you can only have one onyx on a system. This | ||
28 | * should probably be fixed by changing the assumption of | ||
29 | * having just a single card on a system, and making the | ||
30 | * 'card' pointer accessible to anyone who needs it instead | ||
31 | * of hiding it in the aoa_snd_* functions... | ||
32 | * | ||
33 | */ | ||
34 | #include <linux/delay.h> | ||
35 | #include <linux/module.h> | ||
36 | MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>"); | ||
37 | MODULE_LICENSE("GPL"); | ||
38 | MODULE_DESCRIPTION("pcm3052 (onyx) codec driver for snd-aoa"); | ||
39 | |||
40 | #include "snd-aoa-codec-onyx.h" | ||
41 | #include "../aoa.h" | ||
42 | #include "../soundbus/soundbus.h" | ||
43 | |||
44 | |||
45 | #define PFX "snd-aoa-codec-onyx: " | ||
46 | |||
47 | struct onyx { | ||
48 | /* cache registers 65 to 80, they are write-only! */ | ||
49 | u8 cache[16]; | ||
50 | struct i2c_client i2c; | ||
51 | struct aoa_codec codec; | ||
52 | u32 initialised:1, | ||
53 | spdif_locked:1, | ||
54 | analog_locked:1, | ||
55 | original_mute:2; | ||
56 | int open_count; | ||
57 | struct codec_info *codec_info; | ||
58 | |||
59 | /* mutex serializes concurrent access to the device | ||
60 | * and this structure. | ||
61 | */ | ||
62 | struct mutex mutex; | ||
63 | }; | ||
64 | #define codec_to_onyx(c) container_of(c, struct onyx, codec) | ||
65 | |||
66 | /* both return 0 if all ok, else on error */ | ||
67 | static int onyx_read_register(struct onyx *onyx, u8 reg, u8 *value) | ||
68 | { | ||
69 | s32 v; | ||
70 | |||
71 | if (reg != ONYX_REG_CONTROL) { | ||
72 | *value = onyx->cache[reg-FIRSTREGISTER]; | ||
73 | return 0; | ||
74 | } | ||
75 | v = i2c_smbus_read_byte_data(&onyx->i2c, reg); | ||
76 | if (v < 0) | ||
77 | return -1; | ||
78 | *value = (u8)v; | ||
79 | onyx->cache[ONYX_REG_CONTROL-FIRSTREGISTER] = *value; | ||
80 | return 0; | ||
81 | } | ||
82 | |||
83 | static int onyx_write_register(struct onyx *onyx, u8 reg, u8 value) | ||
84 | { | ||
85 | int result; | ||
86 | |||
87 | result = i2c_smbus_write_byte_data(&onyx->i2c, reg, value); | ||
88 | if (!result) | ||
89 | onyx->cache[reg-FIRSTREGISTER] = value; | ||
90 | return result; | ||
91 | } | ||
92 | |||
93 | /* alsa stuff */ | ||
94 | |||
95 | static int onyx_dev_register(struct snd_device *dev) | ||
96 | { | ||
97 | return 0; | ||
98 | } | ||
99 | |||
100 | static struct snd_device_ops ops = { | ||
101 | .dev_register = onyx_dev_register, | ||
102 | }; | ||
103 | |||
104 | /* this is necessary because most alsa mixer programs | ||
105 | * can't properly handle the negative range */ | ||
106 | #define VOLUME_RANGE_SHIFT 128 | ||
107 | |||
108 | static int onyx_snd_vol_info(struct snd_kcontrol *kcontrol, | ||
109 | struct snd_ctl_elem_info *uinfo) | ||
110 | { | ||
111 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
112 | uinfo->count = 2; | ||
113 | uinfo->value.integer.min = -128 + VOLUME_RANGE_SHIFT; | ||
114 | uinfo->value.integer.max = -1 + VOLUME_RANGE_SHIFT; | ||
115 | return 0; | ||
116 | } | ||
117 | |||
118 | static int onyx_snd_vol_get(struct snd_kcontrol *kcontrol, | ||
119 | struct snd_ctl_elem_value *ucontrol) | ||
120 | { | ||
121 | struct onyx *onyx = snd_kcontrol_chip(kcontrol); | ||
122 | s8 l, r; | ||
123 | |||
124 | mutex_lock(&onyx->mutex); | ||
125 | onyx_read_register(onyx, ONYX_REG_DAC_ATTEN_LEFT, &l); | ||
126 | onyx_read_register(onyx, ONYX_REG_DAC_ATTEN_RIGHT, &r); | ||
127 | mutex_unlock(&onyx->mutex); | ||
128 | |||
129 | ucontrol->value.integer.value[0] = l + VOLUME_RANGE_SHIFT; | ||
130 | ucontrol->value.integer.value[1] = r + VOLUME_RANGE_SHIFT; | ||
131 | |||
132 | return 0; | ||
133 | } | ||
134 | |||
135 | static int onyx_snd_vol_put(struct snd_kcontrol *kcontrol, | ||
136 | struct snd_ctl_elem_value *ucontrol) | ||
137 | { | ||
138 | struct onyx *onyx = snd_kcontrol_chip(kcontrol); | ||
139 | s8 l, r; | ||
140 | |||
141 | mutex_lock(&onyx->mutex); | ||
142 | onyx_read_register(onyx, ONYX_REG_DAC_ATTEN_LEFT, &l); | ||
143 | onyx_read_register(onyx, ONYX_REG_DAC_ATTEN_RIGHT, &r); | ||
144 | |||
145 | if (l + VOLUME_RANGE_SHIFT == ucontrol->value.integer.value[0] && | ||
146 | r + VOLUME_RANGE_SHIFT == ucontrol->value.integer.value[1]) { | ||
147 | mutex_unlock(&onyx->mutex); | ||
148 | return 0; | ||
149 | } | ||
150 | |||
151 | onyx_write_register(onyx, ONYX_REG_DAC_ATTEN_LEFT, | ||
152 | ucontrol->value.integer.value[0] | ||
153 | - VOLUME_RANGE_SHIFT); | ||
154 | onyx_write_register(onyx, ONYX_REG_DAC_ATTEN_RIGHT, | ||
155 | ucontrol->value.integer.value[1] | ||
156 | - VOLUME_RANGE_SHIFT); | ||
157 | mutex_unlock(&onyx->mutex); | ||
158 | |||
159 | return 1; | ||
160 | } | ||
161 | |||
162 | static struct snd_kcontrol_new volume_control = { | ||
163 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
164 | .name = "Master Playback Volume", | ||
165 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, | ||
166 | .info = onyx_snd_vol_info, | ||
167 | .get = onyx_snd_vol_get, | ||
168 | .put = onyx_snd_vol_put, | ||
169 | }; | ||
170 | |||
171 | /* like above, this is necessary because a lot | ||
172 | * of alsa mixer programs don't handle ranges | ||
173 | * that don't start at 0 properly. | ||
174 | * even alsamixer is one of them... */ | ||
175 | #define INPUTGAIN_RANGE_SHIFT (-3) | ||
176 | |||
177 | static int onyx_snd_inputgain_info(struct snd_kcontrol *kcontrol, | ||
178 | struct snd_ctl_elem_info *uinfo) | ||
179 | { | ||
180 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
181 | uinfo->count = 1; | ||
182 | uinfo->value.integer.min = 3 + INPUTGAIN_RANGE_SHIFT; | ||
183 | uinfo->value.integer.max = 28 + INPUTGAIN_RANGE_SHIFT; | ||
184 | return 0; | ||
185 | } | ||
186 | |||
187 | static int onyx_snd_inputgain_get(struct snd_kcontrol *kcontrol, | ||
188 | struct snd_ctl_elem_value *ucontrol) | ||
189 | { | ||
190 | struct onyx *onyx = snd_kcontrol_chip(kcontrol); | ||
191 | u8 ig; | ||
192 | |||
193 | mutex_lock(&onyx->mutex); | ||
194 | onyx_read_register(onyx, ONYX_REG_ADC_CONTROL, &ig); | ||
195 | mutex_unlock(&onyx->mutex); | ||
196 | |||
197 | ucontrol->value.integer.value[0] = | ||
198 | (ig & ONYX_ADC_PGA_GAIN_MASK) + INPUTGAIN_RANGE_SHIFT; | ||
199 | |||
200 | return 0; | ||
201 | } | ||
202 | |||
203 | static int onyx_snd_inputgain_put(struct snd_kcontrol *kcontrol, | ||
204 | struct snd_ctl_elem_value *ucontrol) | ||
205 | { | ||
206 | struct onyx *onyx = snd_kcontrol_chip(kcontrol); | ||
207 | u8 v, n; | ||
208 | |||
209 | mutex_lock(&onyx->mutex); | ||
210 | onyx_read_register(onyx, ONYX_REG_ADC_CONTROL, &v); | ||
211 | n = v; | ||
212 | n &= ~ONYX_ADC_PGA_GAIN_MASK; | ||
213 | n |= (ucontrol->value.integer.value[0] - INPUTGAIN_RANGE_SHIFT) | ||
214 | & ONYX_ADC_PGA_GAIN_MASK; | ||
215 | onyx_write_register(onyx, ONYX_REG_ADC_CONTROL, n); | ||
216 | mutex_unlock(&onyx->mutex); | ||
217 | |||
218 | return n != v; | ||
219 | } | ||
220 | |||
221 | static struct snd_kcontrol_new inputgain_control = { | ||
222 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
223 | .name = "Master Capture Volume", | ||
224 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, | ||
225 | .info = onyx_snd_inputgain_info, | ||
226 | .get = onyx_snd_inputgain_get, | ||
227 | .put = onyx_snd_inputgain_put, | ||
228 | }; | ||
229 | |||
230 | static int onyx_snd_capture_source_info(struct snd_kcontrol *kcontrol, | ||
231 | struct snd_ctl_elem_info *uinfo) | ||
232 | { | ||
233 | static char *texts[] = { "Line-In", "Microphone" }; | ||
234 | |||
235 | uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; | ||
236 | uinfo->count = 1; | ||
237 | uinfo->value.enumerated.items = 2; | ||
238 | if (uinfo->value.enumerated.item > 1) | ||
239 | uinfo->value.enumerated.item = 1; | ||
240 | strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); | ||
241 | return 0; | ||
242 | } | ||
243 | |||
244 | static int onyx_snd_capture_source_get(struct snd_kcontrol *kcontrol, | ||
245 | struct snd_ctl_elem_value *ucontrol) | ||
246 | { | ||
247 | struct onyx *onyx = snd_kcontrol_chip(kcontrol); | ||
248 | s8 v; | ||
249 | |||
250 | mutex_lock(&onyx->mutex); | ||
251 | onyx_read_register(onyx, ONYX_REG_ADC_CONTROL, &v); | ||
252 | mutex_unlock(&onyx->mutex); | ||
253 | |||
254 | ucontrol->value.enumerated.item[0] = !!(v&ONYX_ADC_INPUT_MIC); | ||
255 | |||
256 | return 0; | ||
257 | } | ||
258 | |||
259 | static void onyx_set_capture_source(struct onyx *onyx, int mic) | ||
260 | { | ||
261 | s8 v; | ||
262 | |||
263 | mutex_lock(&onyx->mutex); | ||
264 | onyx_read_register(onyx, ONYX_REG_ADC_CONTROL, &v); | ||
265 | v &= ~ONYX_ADC_INPUT_MIC; | ||
266 | if (mic) | ||
267 | v |= ONYX_ADC_INPUT_MIC; | ||
268 | onyx_write_register(onyx, ONYX_REG_ADC_CONTROL, v); | ||
269 | mutex_unlock(&onyx->mutex); | ||
270 | } | ||
271 | |||
272 | static int onyx_snd_capture_source_put(struct snd_kcontrol *kcontrol, | ||
273 | struct snd_ctl_elem_value *ucontrol) | ||
274 | { | ||
275 | onyx_set_capture_source(snd_kcontrol_chip(kcontrol), | ||
276 | ucontrol->value.enumerated.item[0]); | ||
277 | return 1; | ||
278 | } | ||
279 | |||
280 | static struct snd_kcontrol_new capture_source_control = { | ||
281 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
282 | /* If we name this 'Input Source', it properly shows up in | ||
283 | * alsamixer as a selection, * but it's shown under the | ||
284 | * 'Playback' category. | ||
285 | * If I name it 'Capture Source', it shows up in strange | ||
286 | * ways (two bools of which one can be selected at a | ||
287 | * time) but at least it's shown in the 'Capture' | ||
288 | * category. | ||
289 | * I was told that this was due to backward compatibility, | ||
290 | * but I don't understand then why the mangling is *not* | ||
291 | * done when I name it "Input Source"..... | ||
292 | */ | ||
293 | .name = "Capture Source", | ||
294 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, | ||
295 | .info = onyx_snd_capture_source_info, | ||
296 | .get = onyx_snd_capture_source_get, | ||
297 | .put = onyx_snd_capture_source_put, | ||
298 | }; | ||
299 | |||
300 | static int onyx_snd_mute_info(struct snd_kcontrol *kcontrol, | ||
301 | struct snd_ctl_elem_info *uinfo) | ||
302 | { | ||
303 | uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; | ||
304 | uinfo->count = 2; | ||
305 | uinfo->value.integer.min = 0; | ||
306 | uinfo->value.integer.max = 1; | ||
307 | return 0; | ||
308 | } | ||
309 | |||
310 | static int onyx_snd_mute_get(struct snd_kcontrol *kcontrol, | ||
311 | struct snd_ctl_elem_value *ucontrol) | ||
312 | { | ||
313 | struct onyx *onyx = snd_kcontrol_chip(kcontrol); | ||
314 | u8 c; | ||
315 | |||
316 | mutex_lock(&onyx->mutex); | ||
317 | onyx_read_register(onyx, ONYX_REG_DAC_CONTROL, &c); | ||
318 | mutex_unlock(&onyx->mutex); | ||
319 | |||
320 | ucontrol->value.integer.value[0] = !(c & ONYX_MUTE_LEFT); | ||
321 | ucontrol->value.integer.value[1] = !(c & ONYX_MUTE_RIGHT); | ||
322 | |||
323 | return 0; | ||
324 | } | ||
325 | |||
326 | static int onyx_snd_mute_put(struct snd_kcontrol *kcontrol, | ||
327 | struct snd_ctl_elem_value *ucontrol) | ||
328 | { | ||
329 | struct onyx *onyx = snd_kcontrol_chip(kcontrol); | ||
330 | u8 v = 0, c = 0; | ||
331 | int err = -EBUSY; | ||
332 | |||
333 | mutex_lock(&onyx->mutex); | ||
334 | if (onyx->analog_locked) | ||
335 | goto out_unlock; | ||
336 | |||
337 | onyx_read_register(onyx, ONYX_REG_DAC_CONTROL, &v); | ||
338 | c = v; | ||
339 | c &= ~(ONYX_MUTE_RIGHT | ONYX_MUTE_LEFT); | ||
340 | if (!ucontrol->value.integer.value[0]) | ||
341 | c |= ONYX_MUTE_LEFT; | ||
342 | if (!ucontrol->value.integer.value[1]) | ||
343 | c |= ONYX_MUTE_RIGHT; | ||
344 | err = onyx_write_register(onyx, ONYX_REG_DAC_CONTROL, c); | ||
345 | |||
346 | out_unlock: | ||
347 | mutex_unlock(&onyx->mutex); | ||
348 | |||
349 | return !err ? (v != c) : err; | ||
350 | } | ||
351 | |||
352 | static struct snd_kcontrol_new mute_control = { | ||
353 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
354 | .name = "Master Playback Switch", | ||
355 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, | ||
356 | .info = onyx_snd_mute_info, | ||
357 | .get = onyx_snd_mute_get, | ||
358 | .put = onyx_snd_mute_put, | ||
359 | }; | ||
360 | |||
361 | |||
362 | static int onyx_snd_single_bit_info(struct snd_kcontrol *kcontrol, | ||
363 | struct snd_ctl_elem_info *uinfo) | ||
364 | { | ||
365 | uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; | ||
366 | uinfo->count = 1; | ||
367 | uinfo->value.integer.min = 0; | ||
368 | uinfo->value.integer.max = 1; | ||
369 | return 0; | ||
370 | } | ||
371 | |||
372 | #define FLAG_POLARITY_INVERT 1 | ||
373 | #define FLAG_SPDIFLOCK 2 | ||
374 | |||
375 | static int onyx_snd_single_bit_get(struct snd_kcontrol *kcontrol, | ||
376 | struct snd_ctl_elem_value *ucontrol) | ||
377 | { | ||
378 | struct onyx *onyx = snd_kcontrol_chip(kcontrol); | ||
379 | u8 c; | ||
380 | long int pv = kcontrol->private_value; | ||
381 | u8 polarity = (pv >> 16) & FLAG_POLARITY_INVERT; | ||
382 | u8 address = (pv >> 8) & 0xff; | ||
383 | u8 mask = pv & 0xff; | ||
384 | |||
385 | mutex_lock(&onyx->mutex); | ||
386 | onyx_read_register(onyx, address, &c); | ||
387 | mutex_unlock(&onyx->mutex); | ||
388 | |||
389 | ucontrol->value.integer.value[0] = !!(c & mask) ^ polarity; | ||
390 | |||
391 | return 0; | ||
392 | } | ||
393 | |||
394 | static int onyx_snd_single_bit_put(struct snd_kcontrol *kcontrol, | ||
395 | struct snd_ctl_elem_value *ucontrol) | ||
396 | { | ||
397 | struct onyx *onyx = snd_kcontrol_chip(kcontrol); | ||
398 | u8 v = 0, c = 0; | ||
399 | int err; | ||
400 | long int pv = kcontrol->private_value; | ||
401 | u8 polarity = (pv >> 16) & FLAG_POLARITY_INVERT; | ||
402 | u8 spdiflock = (pv >> 16) & FLAG_SPDIFLOCK; | ||
403 | u8 address = (pv >> 8) & 0xff; | ||
404 | u8 mask = pv & 0xff; | ||
405 | |||
406 | mutex_lock(&onyx->mutex); | ||
407 | if (spdiflock && onyx->spdif_locked) { | ||
408 | /* even if alsamixer doesn't care.. */ | ||
409 | err = -EBUSY; | ||
410 | goto out_unlock; | ||
411 | } | ||
412 | onyx_read_register(onyx, address, &v); | ||
413 | c = v; | ||
414 | c &= ~(mask); | ||
415 | if (!!ucontrol->value.integer.value[0] ^ polarity) | ||
416 | c |= mask; | ||
417 | err = onyx_write_register(onyx, address, c); | ||
418 | |||
419 | out_unlock: | ||
420 | mutex_unlock(&onyx->mutex); | ||
421 | |||
422 | return !err ? (v != c) : err; | ||
423 | } | ||
424 | |||
425 | #define SINGLE_BIT(n, type, description, address, mask, flags) \ | ||
426 | static struct snd_kcontrol_new n##_control = { \ | ||
427 | .iface = SNDRV_CTL_ELEM_IFACE_##type, \ | ||
428 | .name = description, \ | ||
429 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \ | ||
430 | .info = onyx_snd_single_bit_info, \ | ||
431 | .get = onyx_snd_single_bit_get, \ | ||
432 | .put = onyx_snd_single_bit_put, \ | ||
433 | .private_value = (flags << 16) | (address << 8) | mask \ | ||
434 | } | ||
435 | |||
436 | SINGLE_BIT(spdif, | ||
437 | MIXER, | ||
438 | SNDRV_CTL_NAME_IEC958("", PLAYBACK, SWITCH), | ||
439 | ONYX_REG_DIG_INFO4, | ||
440 | ONYX_SPDIF_ENABLE, | ||
441 | FLAG_SPDIFLOCK); | ||
442 | SINGLE_BIT(ovr1, | ||
443 | MIXER, | ||
444 | "Oversampling Rate", | ||
445 | ONYX_REG_DAC_CONTROL, | ||
446 | ONYX_OVR1, | ||
447 | 0); | ||
448 | SINGLE_BIT(flt0, | ||
449 | MIXER, | ||
450 | "Fast Digital Filter Rolloff", | ||
451 | ONYX_REG_DAC_FILTER, | ||
452 | ONYX_ROLLOFF_FAST, | ||
453 | FLAG_POLARITY_INVERT); | ||
454 | SINGLE_BIT(hpf, | ||
455 | MIXER, | ||
456 | "Highpass Filter", | ||
457 | ONYX_REG_ADC_HPF_BYPASS, | ||
458 | ONYX_HPF_DISABLE, | ||
459 | FLAG_POLARITY_INVERT); | ||
460 | SINGLE_BIT(dm12, | ||
461 | MIXER, | ||
462 | "Digital De-Emphasis", | ||
463 | ONYX_REG_DAC_DEEMPH, | ||
464 | ONYX_DIGDEEMPH_CTRL, | ||
465 | 0); | ||
466 | |||
467 | static int onyx_spdif_info(struct snd_kcontrol *kcontrol, | ||
468 | struct snd_ctl_elem_info *uinfo) | ||
469 | { | ||
470 | uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; | ||
471 | uinfo->count = 1; | ||
472 | return 0; | ||
473 | } | ||
474 | |||
475 | static int onyx_spdif_mask_get(struct snd_kcontrol *kcontrol, | ||
476 | struct snd_ctl_elem_value *ucontrol) | ||
477 | { | ||
478 | /* datasheet page 30, all others are 0 */ | ||
479 | ucontrol->value.iec958.status[0] = 0x3e; | ||
480 | ucontrol->value.iec958.status[1] = 0xff; | ||
481 | |||
482 | ucontrol->value.iec958.status[3] = 0x3f; | ||
483 | ucontrol->value.iec958.status[4] = 0x0f; | ||
484 | |||
485 | return 0; | ||
486 | } | ||
487 | |||
488 | static struct snd_kcontrol_new onyx_spdif_mask = { | ||
489 | .access = SNDRV_CTL_ELEM_ACCESS_READ, | ||
490 | .iface = SNDRV_CTL_ELEM_IFACE_PCM, | ||
491 | .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK), | ||
492 | .info = onyx_spdif_info, | ||
493 | .get = onyx_spdif_mask_get, | ||
494 | }; | ||
495 | |||
496 | static int onyx_spdif_get(struct snd_kcontrol *kcontrol, | ||
497 | struct snd_ctl_elem_value *ucontrol) | ||
498 | { | ||
499 | struct onyx *onyx = snd_kcontrol_chip(kcontrol); | ||
500 | u8 v; | ||
501 | |||
502 | mutex_lock(&onyx->mutex); | ||
503 | onyx_read_register(onyx, ONYX_REG_DIG_INFO1, &v); | ||
504 | ucontrol->value.iec958.status[0] = v & 0x3e; | ||
505 | |||
506 | onyx_read_register(onyx, ONYX_REG_DIG_INFO2, &v); | ||
507 | ucontrol->value.iec958.status[1] = v; | ||
508 | |||
509 | onyx_read_register(onyx, ONYX_REG_DIG_INFO3, &v); | ||
510 | ucontrol->value.iec958.status[3] = v & 0x3f; | ||
511 | |||
512 | onyx_read_register(onyx, ONYX_REG_DIG_INFO4, &v); | ||
513 | ucontrol->value.iec958.status[4] = v & 0x0f; | ||
514 | mutex_unlock(&onyx->mutex); | ||
515 | |||
516 | return 0; | ||
517 | } | ||
518 | |||
519 | static int onyx_spdif_put(struct snd_kcontrol *kcontrol, | ||
520 | struct snd_ctl_elem_value *ucontrol) | ||
521 | { | ||
522 | struct onyx *onyx = snd_kcontrol_chip(kcontrol); | ||
523 | u8 v; | ||
524 | |||
525 | mutex_lock(&onyx->mutex); | ||
526 | onyx_read_register(onyx, ONYX_REG_DIG_INFO1, &v); | ||
527 | v = (v & ~0x3e) | (ucontrol->value.iec958.status[0] & 0x3e); | ||
528 | onyx_write_register(onyx, ONYX_REG_DIG_INFO1, v); | ||
529 | |||
530 | v = ucontrol->value.iec958.status[1]; | ||
531 | onyx_write_register(onyx, ONYX_REG_DIG_INFO2, v); | ||
532 | |||
533 | onyx_read_register(onyx, ONYX_REG_DIG_INFO3, &v); | ||
534 | v = (v & ~0x3f) | (ucontrol->value.iec958.status[3] & 0x3f); | ||
535 | onyx_write_register(onyx, ONYX_REG_DIG_INFO3, v); | ||
536 | |||
537 | onyx_read_register(onyx, ONYX_REG_DIG_INFO4, &v); | ||
538 | v = (v & ~0x0f) | (ucontrol->value.iec958.status[4] & 0x0f); | ||
539 | onyx_write_register(onyx, ONYX_REG_DIG_INFO4, v); | ||
540 | mutex_unlock(&onyx->mutex); | ||
541 | |||
542 | return 1; | ||
543 | } | ||
544 | |||
545 | static struct snd_kcontrol_new onyx_spdif_ctrl = { | ||
546 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, | ||
547 | .iface = SNDRV_CTL_ELEM_IFACE_PCM, | ||
548 | .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), | ||
549 | .info = onyx_spdif_info, | ||
550 | .get = onyx_spdif_get, | ||
551 | .put = onyx_spdif_put, | ||
552 | }; | ||
553 | |||
554 | /* our registers */ | ||
555 | |||
556 | static u8 register_map[] = { | ||
557 | ONYX_REG_DAC_ATTEN_LEFT, | ||
558 | ONYX_REG_DAC_ATTEN_RIGHT, | ||
559 | ONYX_REG_CONTROL, | ||
560 | ONYX_REG_DAC_CONTROL, | ||
561 | ONYX_REG_DAC_DEEMPH, | ||
562 | ONYX_REG_DAC_FILTER, | ||
563 | ONYX_REG_DAC_OUTPHASE, | ||
564 | ONYX_REG_ADC_CONTROL, | ||
565 | ONYX_REG_ADC_HPF_BYPASS, | ||
566 | ONYX_REG_DIG_INFO1, | ||
567 | ONYX_REG_DIG_INFO2, | ||
568 | ONYX_REG_DIG_INFO3, | ||
569 | ONYX_REG_DIG_INFO4 | ||
570 | }; | ||
571 | |||
572 | static u8 initial_values[ARRAY_SIZE(register_map)] = { | ||
573 | 0x80, 0x80, /* muted */ | ||
574 | ONYX_MRST | ONYX_SRST, /* but handled specially! */ | ||
575 | ONYX_MUTE_LEFT | ONYX_MUTE_RIGHT, | ||
576 | 0, /* no deemphasis */ | ||
577 | ONYX_DAC_FILTER_ALWAYS, | ||
578 | ONYX_OUTPHASE_INVERTED, | ||
579 | (-1 /*dB*/ + 8) & 0xF, /* line in selected, -1 dB gain*/ | ||
580 | ONYX_ADC_HPF_ALWAYS, | ||
581 | (1<<2), /* pcm audio */ | ||
582 | 2, /* category: pcm coder */ | ||
583 | 0, /* sampling frequency 44.1 kHz, clock accuracy level II */ | ||
584 | 1 /* 24 bit depth */ | ||
585 | }; | ||
586 | |||
587 | /* reset registers of chip, either to initial or to previous values */ | ||
588 | static int onyx_register_init(struct onyx *onyx) | ||
589 | { | ||
590 | int i; | ||
591 | u8 val; | ||
592 | u8 regs[sizeof(initial_values)]; | ||
593 | |||
594 | if (!onyx->initialised) { | ||
595 | memcpy(regs, initial_values, sizeof(initial_values)); | ||
596 | if (onyx_read_register(onyx, ONYX_REG_CONTROL, &val)) | ||
597 | return -1; | ||
598 | val &= ~ONYX_SILICONVERSION; | ||
599 | val |= initial_values[3]; | ||
600 | regs[3] = val; | ||
601 | } else { | ||
602 | for (i=0; i<sizeof(register_map); i++) | ||
603 | regs[i] = onyx->cache[register_map[i]-FIRSTREGISTER]; | ||
604 | } | ||
605 | |||
606 | for (i=0; i<sizeof(register_map); i++) { | ||
607 | if (onyx_write_register(onyx, register_map[i], regs[i])) | ||
608 | return -1; | ||
609 | } | ||
610 | onyx->initialised = 1; | ||
611 | return 0; | ||
612 | } | ||
613 | |||
614 | static struct transfer_info onyx_transfers[] = { | ||
615 | /* this is first so we can skip it if no input is present... | ||
616 | * No hardware exists with that, but it's here as an example | ||
617 | * of what to do :) */ | ||
618 | { | ||
619 | /* analog input */ | ||
620 | .formats = SNDRV_PCM_FMTBIT_S8 | | ||
621 | SNDRV_PCM_FMTBIT_S16_BE | | ||
622 | SNDRV_PCM_FMTBIT_S24_BE, | ||
623 | .rates = SNDRV_PCM_RATE_8000_96000, | ||
624 | .transfer_in = 1, | ||
625 | .must_be_clock_source = 0, | ||
626 | .tag = 0, | ||
627 | }, | ||
628 | { | ||
629 | /* if analog and digital are currently off, anything should go, | ||
630 | * so this entry describes everything we can do... */ | ||
631 | .formats = SNDRV_PCM_FMTBIT_S8 | | ||
632 | SNDRV_PCM_FMTBIT_S16_BE | | ||
633 | SNDRV_PCM_FMTBIT_S24_BE | ||
634 | #ifdef SNDRV_PCM_FMTBIT_COMPRESSED_16BE | ||
635 | | SNDRV_PCM_FMTBIT_COMPRESSED_16BE | ||
636 | #endif | ||
637 | , | ||
638 | .rates = SNDRV_PCM_RATE_8000_96000, | ||
639 | .tag = 0, | ||
640 | }, | ||
641 | { | ||
642 | /* analog output */ | ||
643 | .formats = SNDRV_PCM_FMTBIT_S8 | | ||
644 | SNDRV_PCM_FMTBIT_S16_BE | | ||
645 | SNDRV_PCM_FMTBIT_S24_BE, | ||
646 | .rates = SNDRV_PCM_RATE_8000_96000, | ||
647 | .transfer_in = 0, | ||
648 | .must_be_clock_source = 0, | ||
649 | .tag = 1, | ||
650 | }, | ||
651 | { | ||
652 | /* digital pcm output, also possible for analog out */ | ||
653 | .formats = SNDRV_PCM_FMTBIT_S8 | | ||
654 | SNDRV_PCM_FMTBIT_S16_BE | | ||
655 | SNDRV_PCM_FMTBIT_S24_BE, | ||
656 | .rates = SNDRV_PCM_RATE_32000 | | ||
657 | SNDRV_PCM_RATE_44100 | | ||
658 | SNDRV_PCM_RATE_48000, | ||
659 | .transfer_in = 0, | ||
660 | .must_be_clock_source = 0, | ||
661 | .tag = 2, | ||
662 | }, | ||
663 | #ifdef SNDRV_PCM_FMTBIT_COMPRESSED_16BE | ||
664 | Once alsa gets supports for this kind of thing we can add it... | ||
665 | { | ||
666 | /* digital compressed output */ | ||
667 | .formats = SNDRV_PCM_FMTBIT_COMPRESSED_16BE, | ||
668 | .rates = SNDRV_PCM_RATE_32000 | | ||
669 | SNDRV_PCM_RATE_44100 | | ||
670 | SNDRV_PCM_RATE_48000, | ||
671 | .tag = 2, | ||
672 | }, | ||
673 | #endif | ||
674 | {} | ||
675 | }; | ||
676 | |||
677 | static int onyx_usable(struct codec_info_item *cii, | ||
678 | struct transfer_info *ti, | ||
679 | struct transfer_info *out) | ||
680 | { | ||
681 | u8 v; | ||
682 | struct onyx *onyx = cii->codec_data; | ||
683 | int spdif_enabled, analog_enabled; | ||
684 | |||
685 | mutex_lock(&onyx->mutex); | ||
686 | onyx_read_register(onyx, ONYX_REG_DIG_INFO4, &v); | ||
687 | spdif_enabled = !!(v & ONYX_SPDIF_ENABLE); | ||
688 | onyx_read_register(onyx, ONYX_REG_DAC_CONTROL, &v); | ||
689 | analog_enabled = | ||
690 | (v & (ONYX_MUTE_RIGHT|ONYX_MUTE_LEFT)) | ||
691 | != (ONYX_MUTE_RIGHT|ONYX_MUTE_LEFT); | ||
692 | mutex_unlock(&onyx->mutex); | ||
693 | |||
694 | switch (ti->tag) { | ||
695 | case 0: return 1; | ||
696 | case 1: return analog_enabled; | ||
697 | case 2: return spdif_enabled; | ||
698 | } | ||
699 | return 1; | ||
700 | } | ||
701 | |||
702 | static int onyx_prepare(struct codec_info_item *cii, | ||
703 | struct bus_info *bi, | ||
704 | struct snd_pcm_substream *substream) | ||
705 | { | ||
706 | u8 v; | ||
707 | struct onyx *onyx = cii->codec_data; | ||
708 | int err = -EBUSY; | ||
709 | |||
710 | mutex_lock(&onyx->mutex); | ||
711 | |||
712 | #ifdef SNDRV_PCM_FMTBIT_COMPRESSED_16BE | ||
713 | if (substream->runtime->format == SNDRV_PCM_FMTBIT_COMPRESSED_16BE) { | ||
714 | /* mute and lock analog output */ | ||
715 | onyx_read_register(onyx, ONYX_REG_DAC_CONTROL, &v); | ||
716 | if (onyx_write_register(onyx | ||
717 | ONYX_REG_DAC_CONTROL, | ||
718 | v | ONYX_MUTE_RIGHT | ONYX_MUTE_LEFT)) | ||
719 | goto out_unlock; | ||
720 | onyx->analog_locked = 1; | ||
721 | err = 0; | ||
722 | goto out_unlock; | ||
723 | } | ||
724 | #endif | ||
725 | switch (substream->runtime->rate) { | ||
726 | case 32000: | ||
727 | case 44100: | ||
728 | case 48000: | ||
729 | /* these rates are ok for all outputs */ | ||
730 | /* FIXME: program spdif channel control bits here so that | ||
731 | * userspace doesn't have to if it only plays pcm! */ | ||
732 | err = 0; | ||
733 | goto out_unlock; | ||
734 | default: | ||
735 | /* got some rate that the digital output can't do, | ||
736 | * so disable and lock it */ | ||
737 | onyx_read_register(cii->codec_data, ONYX_REG_DIG_INFO4, &v); | ||
738 | if (onyx_write_register(onyx, | ||
739 | ONYX_REG_DIG_INFO4, | ||
740 | v & ~ONYX_SPDIF_ENABLE)) | ||
741 | goto out_unlock; | ||
742 | onyx->spdif_locked = 1; | ||
743 | err = 0; | ||
744 | goto out_unlock; | ||
745 | } | ||
746 | |||
747 | out_unlock: | ||
748 | mutex_unlock(&onyx->mutex); | ||
749 | |||
750 | return err; | ||
751 | } | ||
752 | |||
753 | static int onyx_open(struct codec_info_item *cii, | ||
754 | struct snd_pcm_substream *substream) | ||
755 | { | ||
756 | struct onyx *onyx = cii->codec_data; | ||
757 | |||
758 | mutex_lock(&onyx->mutex); | ||
759 | onyx->open_count++; | ||
760 | mutex_unlock(&onyx->mutex); | ||
761 | |||
762 | return 0; | ||
763 | } | ||
764 | |||
765 | static int onyx_close(struct codec_info_item *cii, | ||
766 | struct snd_pcm_substream *substream) | ||
767 | { | ||
768 | struct onyx *onyx = cii->codec_data; | ||
769 | |||
770 | mutex_lock(&onyx->mutex); | ||
771 | onyx->open_count--; | ||
772 | if (!onyx->open_count) | ||
773 | onyx->spdif_locked = onyx->analog_locked = 0; | ||
774 | mutex_unlock(&onyx->mutex); | ||
775 | |||
776 | return 0; | ||
777 | } | ||
778 | |||
779 | static int onyx_switch_clock(struct codec_info_item *cii, | ||
780 | enum clock_switch what) | ||
781 | { | ||
782 | struct onyx *onyx = cii->codec_data; | ||
783 | |||
784 | mutex_lock(&onyx->mutex); | ||
785 | /* this *MUST* be more elaborate later... */ | ||
786 | switch (what) { | ||
787 | case CLOCK_SWITCH_PREPARE_SLAVE: | ||
788 | onyx->codec.gpio->methods->all_amps_off(onyx->codec.gpio); | ||
789 | break; | ||
790 | case CLOCK_SWITCH_SLAVE: | ||
791 | onyx->codec.gpio->methods->all_amps_restore(onyx->codec.gpio); | ||
792 | break; | ||
793 | default: /* silence warning */ | ||
794 | break; | ||
795 | } | ||
796 | mutex_unlock(&onyx->mutex); | ||
797 | |||
798 | return 0; | ||
799 | } | ||
800 | |||
801 | #ifdef CONFIG_PM | ||
802 | |||
803 | static int onyx_suspend(struct codec_info_item *cii, pm_message_t state) | ||
804 | { | ||
805 | struct onyx *onyx = cii->codec_data; | ||
806 | u8 v; | ||
807 | int err = -ENXIO; | ||
808 | |||
809 | mutex_lock(&onyx->mutex); | ||
810 | if (onyx_read_register(onyx, ONYX_REG_CONTROL, &v)) | ||
811 | goto out_unlock; | ||
812 | onyx_write_register(onyx, ONYX_REG_CONTROL, v | ONYX_ADPSV | ONYX_DAPSV); | ||
813 | /* Apple does a sleep here but the datasheet says to do it on resume */ | ||
814 | err = 0; | ||
815 | out_unlock: | ||
816 | mutex_unlock(&onyx->mutex); | ||
817 | |||
818 | return err; | ||
819 | } | ||
820 | |||
821 | static int onyx_resume(struct codec_info_item *cii) | ||
822 | { | ||
823 | struct onyx *onyx = cii->codec_data; | ||
824 | u8 v; | ||
825 | int err = -ENXIO; | ||
826 | |||
827 | mutex_lock(&onyx->mutex); | ||
828 | /* take codec out of suspend */ | ||
829 | if (onyx_read_register(onyx, ONYX_REG_CONTROL, &v)) | ||
830 | goto out_unlock; | ||
831 | onyx_write_register(onyx, ONYX_REG_CONTROL, v & ~(ONYX_ADPSV | ONYX_DAPSV)); | ||
832 | /* FIXME: should divide by sample rate, but 8k is the lowest we go */ | ||
833 | msleep(2205000/8000); | ||
834 | /* reset all values */ | ||
835 | onyx_register_init(onyx); | ||
836 | err = 0; | ||
837 | out_unlock: | ||
838 | mutex_unlock(&onyx->mutex); | ||
839 | |||
840 | return err; | ||
841 | } | ||
842 | |||
843 | #endif /* CONFIG_PM */ | ||
844 | |||
845 | static struct codec_info onyx_codec_info = { | ||
846 | .transfers = onyx_transfers, | ||
847 | .sysclock_factor = 256, | ||
848 | .bus_factor = 64, | ||
849 | .owner = THIS_MODULE, | ||
850 | .usable = onyx_usable, | ||
851 | .prepare = onyx_prepare, | ||
852 | .open = onyx_open, | ||
853 | .close = onyx_close, | ||
854 | .switch_clock = onyx_switch_clock, | ||
855 | #ifdef CONFIG_PM | ||
856 | .suspend = onyx_suspend, | ||
857 | .resume = onyx_resume, | ||
858 | #endif | ||
859 | }; | ||
860 | |||
861 | static int onyx_init_codec(struct aoa_codec *codec) | ||
862 | { | ||
863 | struct onyx *onyx = codec_to_onyx(codec); | ||
864 | struct snd_kcontrol *ctl; | ||
865 | struct codec_info *ci = &onyx_codec_info; | ||
866 | u8 v; | ||
867 | int err; | ||
868 | |||
869 | if (!onyx->codec.gpio || !onyx->codec.gpio->methods) { | ||
870 | printk(KERN_ERR PFX "gpios not assigned!!\n"); | ||
871 | return -EINVAL; | ||
872 | } | ||
873 | |||
874 | onyx->codec.gpio->methods->set_hw_reset(onyx->codec.gpio, 0); | ||
875 | msleep(1); | ||
876 | onyx->codec.gpio->methods->set_hw_reset(onyx->codec.gpio, 1); | ||
877 | msleep(1); | ||
878 | onyx->codec.gpio->methods->set_hw_reset(onyx->codec.gpio, 0); | ||
879 | msleep(1); | ||
880 | |||
881 | if (onyx_register_init(onyx)) { | ||
882 | printk(KERN_ERR PFX "failed to initialise onyx registers\n"); | ||
883 | return -ENODEV; | ||
884 | } | ||
885 | |||
886 | if (aoa_snd_device_new(SNDRV_DEV_LOWLEVEL, onyx, &ops)) { | ||
887 | printk(KERN_ERR PFX "failed to create onyx snd device!\n"); | ||
888 | return -ENODEV; | ||
889 | } | ||
890 | |||
891 | /* nothing connected? what a joke! */ | ||
892 | if ((onyx->codec.connected & 0xF) == 0) | ||
893 | return -ENOTCONN; | ||
894 | |||
895 | /* if no inputs are present... */ | ||
896 | if ((onyx->codec.connected & 0xC) == 0) { | ||
897 | if (!onyx->codec_info) | ||
898 | onyx->codec_info = kmalloc(sizeof(struct codec_info), GFP_KERNEL); | ||
899 | if (!onyx->codec_info) | ||
900 | return -ENOMEM; | ||
901 | ci = onyx->codec_info; | ||
902 | *ci = onyx_codec_info; | ||
903 | ci->transfers++; | ||
904 | } | ||
905 | |||
906 | /* if no outputs are present... */ | ||
907 | if ((onyx->codec.connected & 3) == 0) { | ||
908 | if (!onyx->codec_info) | ||
909 | onyx->codec_info = kmalloc(sizeof(struct codec_info), GFP_KERNEL); | ||
910 | if (!onyx->codec_info) | ||
911 | return -ENOMEM; | ||
912 | ci = onyx->codec_info; | ||
913 | /* this is fine as there have to be inputs | ||
914 | * if we end up in this part of the code */ | ||
915 | *ci = onyx_codec_info; | ||
916 | ci->transfers[1].formats = 0; | ||
917 | } | ||
918 | |||
919 | if (onyx->codec.soundbus_dev->attach_codec(onyx->codec.soundbus_dev, | ||
920 | aoa_get_card(), | ||
921 | ci, onyx)) { | ||
922 | printk(KERN_ERR PFX "error creating onyx pcm\n"); | ||
923 | return -ENODEV; | ||
924 | } | ||
925 | #define ADDCTL(n) \ | ||
926 | do { \ | ||
927 | ctl = snd_ctl_new1(&n, onyx); \ | ||
928 | if (ctl) { \ | ||
929 | ctl->id.device = \ | ||
930 | onyx->codec.soundbus_dev->pcm->device; \ | ||
931 | err = aoa_snd_ctl_add(ctl); \ | ||
932 | if (err) \ | ||
933 | goto error; \ | ||
934 | } \ | ||
935 | } while (0) | ||
936 | |||
937 | if (onyx->codec.soundbus_dev->pcm) { | ||
938 | /* give the user appropriate controls | ||
939 | * depending on what inputs are connected */ | ||
940 | if ((onyx->codec.connected & 0xC) == 0xC) | ||
941 | ADDCTL(capture_source_control); | ||
942 | else if (onyx->codec.connected & 4) | ||
943 | onyx_set_capture_source(onyx, 0); | ||
944 | else | ||
945 | onyx_set_capture_source(onyx, 1); | ||
946 | if (onyx->codec.connected & 0xC) | ||
947 | ADDCTL(inputgain_control); | ||
948 | |||
949 | /* depending on what output is connected, | ||
950 | * give the user appropriate controls */ | ||
951 | if (onyx->codec.connected & 1) { | ||
952 | ADDCTL(volume_control); | ||
953 | ADDCTL(mute_control); | ||
954 | ADDCTL(ovr1_control); | ||
955 | ADDCTL(flt0_control); | ||
956 | ADDCTL(hpf_control); | ||
957 | ADDCTL(dm12_control); | ||
958 | /* spdif control defaults to off */ | ||
959 | } | ||
960 | if (onyx->codec.connected & 2) { | ||
961 | ADDCTL(onyx_spdif_mask); | ||
962 | ADDCTL(onyx_spdif_ctrl); | ||
963 | } | ||
964 | if ((onyx->codec.connected & 3) == 3) | ||
965 | ADDCTL(spdif_control); | ||
966 | /* if only S/PDIF is connected, enable it unconditionally */ | ||
967 | if ((onyx->codec.connected & 3) == 2) { | ||
968 | onyx_read_register(onyx, ONYX_REG_DIG_INFO4, &v); | ||
969 | v |= ONYX_SPDIF_ENABLE; | ||
970 | onyx_write_register(onyx, ONYX_REG_DIG_INFO4, v); | ||
971 | } | ||
972 | } | ||
973 | #undef ADDCTL | ||
974 | printk(KERN_INFO PFX "attached to onyx codec via i2c\n"); | ||
975 | |||
976 | return 0; | ||
977 | error: | ||
978 | onyx->codec.soundbus_dev->detach_codec(onyx->codec.soundbus_dev, onyx); | ||
979 | snd_device_free(aoa_get_card(), onyx); | ||
980 | return err; | ||
981 | } | ||
982 | |||
983 | static void onyx_exit_codec(struct aoa_codec *codec) | ||
984 | { | ||
985 | struct onyx *onyx = codec_to_onyx(codec); | ||
986 | |||
987 | if (!onyx->codec.soundbus_dev) { | ||
988 | printk(KERN_ERR PFX "onyx_exit_codec called without soundbus_dev!\n"); | ||
989 | return; | ||
990 | } | ||
991 | onyx->codec.soundbus_dev->detach_codec(onyx->codec.soundbus_dev, onyx); | ||
992 | } | ||
993 | |||
994 | static struct i2c_driver onyx_driver; | ||
995 | |||
996 | static int onyx_create(struct i2c_adapter *adapter, | ||
997 | struct device_node *node, | ||
998 | int addr) | ||
999 | { | ||
1000 | struct onyx *onyx; | ||
1001 | u8 dummy; | ||
1002 | |||
1003 | onyx = kzalloc(sizeof(struct onyx), GFP_KERNEL); | ||
1004 | |||
1005 | if (!onyx) | ||
1006 | return -ENOMEM; | ||
1007 | |||
1008 | mutex_init(&onyx->mutex); | ||
1009 | onyx->i2c.driver = &onyx_driver; | ||
1010 | onyx->i2c.adapter = adapter; | ||
1011 | onyx->i2c.addr = addr & 0x7f; | ||
1012 | strlcpy(onyx->i2c.name, "onyx audio codec", I2C_NAME_SIZE-1); | ||
1013 | |||
1014 | if (i2c_attach_client(&onyx->i2c)) { | ||
1015 | printk(KERN_ERR PFX "failed to attach to i2c\n"); | ||
1016 | goto fail; | ||
1017 | } | ||
1018 | |||
1019 | /* we try to read from register ONYX_REG_CONTROL | ||
1020 | * to check if the codec is present */ | ||
1021 | if (onyx_read_register(onyx, ONYX_REG_CONTROL, &dummy) != 0) { | ||
1022 | i2c_detach_client(&onyx->i2c); | ||
1023 | printk(KERN_ERR PFX "failed to read control register\n"); | ||
1024 | goto fail; | ||
1025 | } | ||
1026 | |||
1027 | strlcpy(onyx->codec.name, "onyx", MAX_CODEC_NAME_LEN-1); | ||
1028 | onyx->codec.owner = THIS_MODULE; | ||
1029 | onyx->codec.init = onyx_init_codec; | ||
1030 | onyx->codec.exit = onyx_exit_codec; | ||
1031 | onyx->codec.node = of_node_get(node); | ||
1032 | |||
1033 | if (aoa_codec_register(&onyx->codec)) { | ||
1034 | i2c_detach_client(&onyx->i2c); | ||
1035 | goto fail; | ||
1036 | } | ||
1037 | printk(KERN_DEBUG PFX "created and attached onyx instance\n"); | ||
1038 | return 0; | ||
1039 | fail: | ||
1040 | kfree(onyx); | ||
1041 | return -EINVAL; | ||
1042 | } | ||
1043 | |||
1044 | static int onyx_i2c_attach(struct i2c_adapter *adapter) | ||
1045 | { | ||
1046 | struct device_node *busnode, *dev = NULL; | ||
1047 | struct pmac_i2c_bus *bus; | ||
1048 | |||
1049 | bus = pmac_i2c_adapter_to_bus(adapter); | ||
1050 | if (bus == NULL) | ||
1051 | return -ENODEV; | ||
1052 | busnode = pmac_i2c_get_bus_node(bus); | ||
1053 | |||
1054 | while ((dev = of_get_next_child(busnode, dev)) != NULL) { | ||
1055 | if (device_is_compatible(dev, "pcm3052")) { | ||
1056 | u32 *addr; | ||
1057 | printk(KERN_DEBUG PFX "found pcm3052\n"); | ||
1058 | addr = (u32 *) get_property(dev, "reg", NULL); | ||
1059 | if (!addr) | ||
1060 | return -ENODEV; | ||
1061 | return onyx_create(adapter, dev, (*addr)>>1); | ||
1062 | } | ||
1063 | } | ||
1064 | |||
1065 | /* if that didn't work, try desperate mode for older | ||
1066 | * machines that have stuff missing from the device tree */ | ||
1067 | |||
1068 | if (!device_is_compatible(busnode, "k2-i2c")) | ||
1069 | return -ENODEV; | ||
1070 | |||
1071 | printk(KERN_DEBUG PFX "found k2-i2c, checking if onyx chip is on it\n"); | ||
1072 | /* probe both possible addresses for the onyx chip */ | ||
1073 | if (onyx_create(adapter, NULL, 0x46) == 0) | ||
1074 | return 0; | ||
1075 | return onyx_create(adapter, NULL, 0x47); | ||
1076 | } | ||
1077 | |||
1078 | static int onyx_i2c_detach(struct i2c_client *client) | ||
1079 | { | ||
1080 | struct onyx *onyx = container_of(client, struct onyx, i2c); | ||
1081 | int err; | ||
1082 | |||
1083 | if ((err = i2c_detach_client(client))) | ||
1084 | return err; | ||
1085 | aoa_codec_unregister(&onyx->codec); | ||
1086 | of_node_put(onyx->codec.node); | ||
1087 | if (onyx->codec_info) | ||
1088 | kfree(onyx->codec_info); | ||
1089 | kfree(onyx); | ||
1090 | return 0; | ||
1091 | } | ||
1092 | |||
1093 | static struct i2c_driver onyx_driver = { | ||
1094 | .driver = { | ||
1095 | .name = "aoa_codec_onyx", | ||
1096 | .owner = THIS_MODULE, | ||
1097 | }, | ||
1098 | .attach_adapter = onyx_i2c_attach, | ||
1099 | .detach_client = onyx_i2c_detach, | ||
1100 | }; | ||
1101 | |||
1102 | static int __init onyx_init(void) | ||
1103 | { | ||
1104 | return i2c_add_driver(&onyx_driver); | ||
1105 | } | ||
1106 | |||
1107 | static void __exit onyx_exit(void) | ||
1108 | { | ||
1109 | i2c_del_driver(&onyx_driver); | ||
1110 | } | ||
1111 | |||
1112 | module_init(onyx_init); | ||
1113 | module_exit(onyx_exit); | ||
diff --git a/sound/aoa/codecs/snd-aoa-codec-onyx.h b/sound/aoa/codecs/snd-aoa-codec-onyx.h new file mode 100644 index 000000000000..aeedda773699 --- /dev/null +++ b/sound/aoa/codecs/snd-aoa-codec-onyx.h | |||
@@ -0,0 +1,76 @@ | |||
1 | /* | ||
2 | * Apple Onboard Audio driver for Onyx codec (header) | ||
3 | * | ||
4 | * Copyright 2006 Johannes Berg <johannes@sipsolutions.net> | ||
5 | * | ||
6 | * GPL v2, can be found in COPYING. | ||
7 | */ | ||
8 | #ifndef __SND_AOA_CODEC_ONYX_H | ||
9 | #define __SND_AOA_CODEC_ONYX_H | ||
10 | #include <stddef.h> | ||
11 | #include <linux/i2c.h> | ||
12 | #include <linux/i2c-dev.h> | ||
13 | #include <asm/pmac_low_i2c.h> | ||
14 | #include <asm/prom.h> | ||
15 | |||
16 | /* PCM3052 register definitions */ | ||
17 | |||
18 | /* the attenuation registers take values from | ||
19 | * -1 (0dB) to -127 (-63.0 dB) or others (muted) */ | ||
20 | #define ONYX_REG_DAC_ATTEN_LEFT 65 | ||
21 | #define FIRSTREGISTER ONYX_REG_DAC_ATTEN_LEFT | ||
22 | #define ONYX_REG_DAC_ATTEN_RIGHT 66 | ||
23 | |||
24 | #define ONYX_REG_CONTROL 67 | ||
25 | # define ONYX_MRST (1<<7) | ||
26 | # define ONYX_SRST (1<<6) | ||
27 | # define ONYX_ADPSV (1<<5) | ||
28 | # define ONYX_DAPSV (1<<4) | ||
29 | # define ONYX_SILICONVERSION (1<<0) | ||
30 | /* all others reserved */ | ||
31 | |||
32 | #define ONYX_REG_DAC_CONTROL 68 | ||
33 | # define ONYX_OVR1 (1<<6) | ||
34 | # define ONYX_MUTE_RIGHT (1<<1) | ||
35 | # define ONYX_MUTE_LEFT (1<<0) | ||
36 | |||
37 | #define ONYX_REG_DAC_DEEMPH 69 | ||
38 | # define ONYX_DIGDEEMPH_SHIFT 5 | ||
39 | # define ONYX_DIGDEEMPH_MASK (3<<ONYX_DIGDEEMPH_SHIFT) | ||
40 | # define ONYX_DIGDEEMPH_CTRL (1<<4) | ||
41 | |||
42 | #define ONYX_REG_DAC_FILTER 70 | ||
43 | # define ONYX_ROLLOFF_FAST (1<<5) | ||
44 | # define ONYX_DAC_FILTER_ALWAYS (1<<2) | ||
45 | |||
46 | #define ONYX_REG_DAC_OUTPHASE 71 | ||
47 | # define ONYX_OUTPHASE_INVERTED (1<<0) | ||
48 | |||
49 | #define ONYX_REG_ADC_CONTROL 72 | ||
50 | # define ONYX_ADC_INPUT_MIC (1<<5) | ||
51 | /* 8 + input gain in dB, valid range for input gain is -4 .. 20 dB */ | ||
52 | # define ONYX_ADC_PGA_GAIN_MASK 0x1f | ||
53 | |||
54 | #define ONYX_REG_ADC_HPF_BYPASS 75 | ||
55 | # define ONYX_HPF_DISABLE (1<<3) | ||
56 | # define ONYX_ADC_HPF_ALWAYS (1<<2) | ||
57 | |||
58 | #define ONYX_REG_DIG_INFO1 77 | ||
59 | # define ONYX_MASK_DIN_TO_BPZ (1<<7) | ||
60 | /* bits 1-5 control channel bits 1-5 */ | ||
61 | # define ONYX_DIGOUT_DISABLE (1<<0) | ||
62 | |||
63 | #define ONYX_REG_DIG_INFO2 78 | ||
64 | /* controls channel bits 8-15 */ | ||
65 | |||
66 | #define ONYX_REG_DIG_INFO3 79 | ||
67 | /* control channel bits 24-29, high 2 bits reserved */ | ||
68 | |||
69 | #define ONYX_REG_DIG_INFO4 80 | ||
70 | # define ONYX_VALIDL (1<<7) | ||
71 | # define ONYX_VALIDR (1<<6) | ||
72 | # define ONYX_SPDIF_ENABLE (1<<5) | ||
73 | /* lower 4 bits control bits 32-35 of channel control and word length */ | ||
74 | # define ONYX_WORDLEN_MASK (0xF) | ||
75 | |||
76 | #endif /* __SND_AOA_CODEC_ONYX_H */ | ||
diff --git a/sound/aoa/codecs/snd-aoa-codec-tas-gain-table.h b/sound/aoa/codecs/snd-aoa-codec-tas-gain-table.h new file mode 100644 index 000000000000..4cfa6757715e --- /dev/null +++ b/sound/aoa/codecs/snd-aoa-codec-tas-gain-table.h | |||
@@ -0,0 +1,209 @@ | |||
1 | /* | ||
2 | This is the program used to generate below table. | ||
3 | |||
4 | #include <stdio.h> | ||
5 | #include <math.h> | ||
6 | int main() { | ||
7 | int dB2; | ||
8 | printf("/" "* This file is only included exactly once!\n"); | ||
9 | printf(" *\n"); | ||
10 | printf(" * If they'd only tell us that generating this table was\n"); | ||
11 | printf(" * as easy as calculating\n"); | ||
12 | printf(" * hwvalue = 1048576.0*exp(0.057564628*dB*2)\n"); | ||
13 | printf(" * :) *" "/\n"); | ||
14 | printf("static int tas_gaintable[] = {\n"); | ||
15 | printf(" 0x000000, /" "* -infinity dB *" "/\n"); | ||
16 | for (dB2=-140;dB2<=36;dB2++) | ||
17 | printf(" 0x%.6x, /" "* %-02.1f dB *" "/\n", (int)(1048576.0*exp(0.057564628*dB2)), dB2/2.0); | ||
18 | printf("};\n\n"); | ||
19 | } | ||
20 | |||
21 | */ | ||
22 | |||
23 | /* This file is only included exactly once! | ||
24 | * | ||
25 | * If they'd only tell us that generating this table was | ||
26 | * as easy as calculating | ||
27 | * hwvalue = 1048576.0*exp(0.057564628*dB*2) | ||
28 | * :) */ | ||
29 | static int tas_gaintable[] = { | ||
30 | 0x000000, /* -infinity dB */ | ||
31 | 0x00014b, /* -70.0 dB */ | ||
32 | 0x00015f, /* -69.5 dB */ | ||
33 | 0x000174, /* -69.0 dB */ | ||
34 | 0x00018a, /* -68.5 dB */ | ||
35 | 0x0001a1, /* -68.0 dB */ | ||
36 | 0x0001ba, /* -67.5 dB */ | ||
37 | 0x0001d4, /* -67.0 dB */ | ||
38 | 0x0001f0, /* -66.5 dB */ | ||
39 | 0x00020d, /* -66.0 dB */ | ||
40 | 0x00022c, /* -65.5 dB */ | ||
41 | 0x00024d, /* -65.0 dB */ | ||
42 | 0x000270, /* -64.5 dB */ | ||
43 | 0x000295, /* -64.0 dB */ | ||
44 | 0x0002bc, /* -63.5 dB */ | ||
45 | 0x0002e6, /* -63.0 dB */ | ||
46 | 0x000312, /* -62.5 dB */ | ||
47 | 0x000340, /* -62.0 dB */ | ||
48 | 0x000372, /* -61.5 dB */ | ||
49 | 0x0003a6, /* -61.0 dB */ | ||
50 | 0x0003dd, /* -60.5 dB */ | ||
51 | 0x000418, /* -60.0 dB */ | ||
52 | 0x000456, /* -59.5 dB */ | ||
53 | 0x000498, /* -59.0 dB */ | ||
54 | 0x0004de, /* -58.5 dB */ | ||
55 | 0x000528, /* -58.0 dB */ | ||
56 | 0x000576, /* -57.5 dB */ | ||
57 | 0x0005c9, /* -57.0 dB */ | ||
58 | 0x000620, /* -56.5 dB */ | ||
59 | 0x00067d, /* -56.0 dB */ | ||
60 | 0x0006e0, /* -55.5 dB */ | ||
61 | 0x000748, /* -55.0 dB */ | ||
62 | 0x0007b7, /* -54.5 dB */ | ||
63 | 0x00082c, /* -54.0 dB */ | ||
64 | 0x0008a8, /* -53.5 dB */ | ||
65 | 0x00092b, /* -53.0 dB */ | ||
66 | 0x0009b6, /* -52.5 dB */ | ||
67 | 0x000a49, /* -52.0 dB */ | ||
68 | 0x000ae5, /* -51.5 dB */ | ||
69 | 0x000b8b, /* -51.0 dB */ | ||
70 | 0x000c3a, /* -50.5 dB */ | ||
71 | 0x000cf3, /* -50.0 dB */ | ||
72 | 0x000db8, /* -49.5 dB */ | ||
73 | 0x000e88, /* -49.0 dB */ | ||
74 | 0x000f64, /* -48.5 dB */ | ||
75 | 0x00104e, /* -48.0 dB */ | ||
76 | 0x001145, /* -47.5 dB */ | ||
77 | 0x00124b, /* -47.0 dB */ | ||
78 | 0x001361, /* -46.5 dB */ | ||
79 | 0x001487, /* -46.0 dB */ | ||
80 | 0x0015be, /* -45.5 dB */ | ||
81 | 0x001708, /* -45.0 dB */ | ||
82 | 0x001865, /* -44.5 dB */ | ||
83 | 0x0019d8, /* -44.0 dB */ | ||
84 | 0x001b60, /* -43.5 dB */ | ||
85 | 0x001cff, /* -43.0 dB */ | ||
86 | 0x001eb7, /* -42.5 dB */ | ||
87 | 0x002089, /* -42.0 dB */ | ||
88 | 0x002276, /* -41.5 dB */ | ||
89 | 0x002481, /* -41.0 dB */ | ||
90 | 0x0026ab, /* -40.5 dB */ | ||
91 | 0x0028f5, /* -40.0 dB */ | ||
92 | 0x002b63, /* -39.5 dB */ | ||
93 | 0x002df5, /* -39.0 dB */ | ||
94 | 0x0030ae, /* -38.5 dB */ | ||
95 | 0x003390, /* -38.0 dB */ | ||
96 | 0x00369e, /* -37.5 dB */ | ||
97 | 0x0039db, /* -37.0 dB */ | ||
98 | 0x003d49, /* -36.5 dB */ | ||
99 | 0x0040ea, /* -36.0 dB */ | ||
100 | 0x0044c3, /* -35.5 dB */ | ||
101 | 0x0048d6, /* -35.0 dB */ | ||
102 | 0x004d27, /* -34.5 dB */ | ||
103 | 0x0051b9, /* -34.0 dB */ | ||
104 | 0x005691, /* -33.5 dB */ | ||
105 | 0x005bb2, /* -33.0 dB */ | ||
106 | 0x006121, /* -32.5 dB */ | ||
107 | 0x0066e3, /* -32.0 dB */ | ||
108 | 0x006cfb, /* -31.5 dB */ | ||
109 | 0x007370, /* -31.0 dB */ | ||
110 | 0x007a48, /* -30.5 dB */ | ||
111 | 0x008186, /* -30.0 dB */ | ||
112 | 0x008933, /* -29.5 dB */ | ||
113 | 0x009154, /* -29.0 dB */ | ||
114 | 0x0099f1, /* -28.5 dB */ | ||
115 | 0x00a310, /* -28.0 dB */ | ||
116 | 0x00acba, /* -27.5 dB */ | ||
117 | 0x00b6f6, /* -27.0 dB */ | ||
118 | 0x00c1cd, /* -26.5 dB */ | ||
119 | 0x00cd49, /* -26.0 dB */ | ||
120 | 0x00d973, /* -25.5 dB */ | ||
121 | 0x00e655, /* -25.0 dB */ | ||
122 | 0x00f3fb, /* -24.5 dB */ | ||
123 | 0x010270, /* -24.0 dB */ | ||
124 | 0x0111c0, /* -23.5 dB */ | ||
125 | 0x0121f9, /* -23.0 dB */ | ||
126 | 0x013328, /* -22.5 dB */ | ||
127 | 0x01455b, /* -22.0 dB */ | ||
128 | 0x0158a2, /* -21.5 dB */ | ||
129 | 0x016d0e, /* -21.0 dB */ | ||
130 | 0x0182af, /* -20.5 dB */ | ||
131 | 0x019999, /* -20.0 dB */ | ||
132 | 0x01b1de, /* -19.5 dB */ | ||
133 | 0x01cb94, /* -19.0 dB */ | ||
134 | 0x01e6cf, /* -18.5 dB */ | ||
135 | 0x0203a7, /* -18.0 dB */ | ||
136 | 0x022235, /* -17.5 dB */ | ||
137 | 0x024293, /* -17.0 dB */ | ||
138 | 0x0264db, /* -16.5 dB */ | ||
139 | 0x02892c, /* -16.0 dB */ | ||
140 | 0x02afa3, /* -15.5 dB */ | ||
141 | 0x02d862, /* -15.0 dB */ | ||
142 | 0x03038a, /* -14.5 dB */ | ||
143 | 0x033142, /* -14.0 dB */ | ||
144 | 0x0361af, /* -13.5 dB */ | ||
145 | 0x0394fa, /* -13.0 dB */ | ||
146 | 0x03cb50, /* -12.5 dB */ | ||
147 | 0x0404de, /* -12.0 dB */ | ||
148 | 0x0441d5, /* -11.5 dB */ | ||
149 | 0x048268, /* -11.0 dB */ | ||
150 | 0x04c6d0, /* -10.5 dB */ | ||
151 | 0x050f44, /* -10.0 dB */ | ||
152 | 0x055c04, /* -9.5 dB */ | ||
153 | 0x05ad50, /* -9.0 dB */ | ||
154 | 0x06036e, /* -8.5 dB */ | ||
155 | 0x065ea5, /* -8.0 dB */ | ||
156 | 0x06bf44, /* -7.5 dB */ | ||
157 | 0x07259d, /* -7.0 dB */ | ||
158 | 0x079207, /* -6.5 dB */ | ||
159 | 0x0804dc, /* -6.0 dB */ | ||
160 | 0x087e80, /* -5.5 dB */ | ||
161 | 0x08ff59, /* -5.0 dB */ | ||
162 | 0x0987d5, /* -4.5 dB */ | ||
163 | 0x0a1866, /* -4.0 dB */ | ||
164 | 0x0ab189, /* -3.5 dB */ | ||
165 | 0x0b53be, /* -3.0 dB */ | ||
166 | 0x0bff91, /* -2.5 dB */ | ||
167 | 0x0cb591, /* -2.0 dB */ | ||
168 | 0x0d765a, /* -1.5 dB */ | ||
169 | 0x0e4290, /* -1.0 dB */ | ||
170 | 0x0f1adf, /* -0.5 dB */ | ||
171 | 0x100000, /* 0.0 dB */ | ||
172 | 0x10f2b4, /* 0.5 dB */ | ||
173 | 0x11f3c9, /* 1.0 dB */ | ||
174 | 0x13041a, /* 1.5 dB */ | ||
175 | 0x14248e, /* 2.0 dB */ | ||
176 | 0x15561a, /* 2.5 dB */ | ||
177 | 0x1699c0, /* 3.0 dB */ | ||
178 | 0x17f094, /* 3.5 dB */ | ||
179 | 0x195bb8, /* 4.0 dB */ | ||
180 | 0x1adc61, /* 4.5 dB */ | ||
181 | 0x1c73d5, /* 5.0 dB */ | ||
182 | 0x1e236d, /* 5.5 dB */ | ||
183 | 0x1fec98, /* 6.0 dB */ | ||
184 | 0x21d0d9, /* 6.5 dB */ | ||
185 | 0x23d1cd, /* 7.0 dB */ | ||
186 | 0x25f125, /* 7.5 dB */ | ||
187 | 0x2830af, /* 8.0 dB */ | ||
188 | 0x2a9254, /* 8.5 dB */ | ||
189 | 0x2d1818, /* 9.0 dB */ | ||
190 | 0x2fc420, /* 9.5 dB */ | ||
191 | 0x3298b0, /* 10.0 dB */ | ||
192 | 0x35982f, /* 10.5 dB */ | ||
193 | 0x38c528, /* 11.0 dB */ | ||
194 | 0x3c224c, /* 11.5 dB */ | ||
195 | 0x3fb278, /* 12.0 dB */ | ||
196 | 0x4378b0, /* 12.5 dB */ | ||
197 | 0x477829, /* 13.0 dB */ | ||
198 | 0x4bb446, /* 13.5 dB */ | ||
199 | 0x5030a1, /* 14.0 dB */ | ||
200 | 0x54f106, /* 14.5 dB */ | ||
201 | 0x59f980, /* 15.0 dB */ | ||
202 | 0x5f4e52, /* 15.5 dB */ | ||
203 | 0x64f403, /* 16.0 dB */ | ||
204 | 0x6aef5e, /* 16.5 dB */ | ||
205 | 0x714575, /* 17.0 dB */ | ||
206 | 0x77fbaa, /* 17.5 dB */ | ||
207 | 0x7f17af, /* 18.0 dB */ | ||
208 | }; | ||
209 | |||
diff --git a/sound/aoa/codecs/snd-aoa-codec-tas.c b/sound/aoa/codecs/snd-aoa-codec-tas.c new file mode 100644 index 000000000000..2e39ff6ee349 --- /dev/null +++ b/sound/aoa/codecs/snd-aoa-codec-tas.c | |||
@@ -0,0 +1,654 @@ | |||
1 | /* | ||
2 | * Apple Onboard Audio driver for tas codec | ||
3 | * | ||
4 | * Copyright 2006 Johannes Berg <johannes@sipsolutions.net> | ||
5 | * | ||
6 | * GPL v2, can be found in COPYING. | ||
7 | * | ||
8 | * Open questions: | ||
9 | * - How to distinguish between 3004 and versions? | ||
10 | * | ||
11 | * FIXMEs: | ||
12 | * - This codec driver doesn't honour the 'connected' | ||
13 | * property of the aoa_codec struct, hence if | ||
14 | * it is used in machines where not everything is | ||
15 | * connected it will display wrong mixer elements. | ||
16 | * - Driver assumes that the microphone is always | ||
17 | * monaureal and connected to the right channel of | ||
18 | * the input. This should also be a codec-dependent | ||
19 | * flag, maybe the codec should have 3 different | ||
20 | * bits for the three different possibilities how | ||
21 | * it can be hooked up... | ||
22 | * But as long as I don't see any hardware hooked | ||
23 | * up that way... | ||
24 | * - As Apple notes in their code, the tas3004 seems | ||
25 | * to delay the right channel by one sample. You can | ||
26 | * see this when for example recording stereo in | ||
27 | * audacity, or recording the tas output via cable | ||
28 | * on another machine (use a sinus generator or so). | ||
29 | * I tried programming the BiQuads but couldn't | ||
30 | * make the delay work, maybe someone can read the | ||
31 | * datasheet and fix it. The relevant Apple comment | ||
32 | * is in AppleTAS3004Audio.cpp lines 1637 ff. Note | ||
33 | * that their comment describing how they program | ||
34 | * the filters sucks... | ||
35 | * | ||
36 | * Other things: | ||
37 | * - this should actually register *two* aoa_codec | ||
38 | * structs since it has two inputs. Then it must | ||
39 | * use the prepare callback to forbid running the | ||
40 | * secondary output on a different clock. | ||
41 | * Also, whatever bus knows how to do this must | ||
42 | * provide two soundbus_dev devices and the fabric | ||
43 | * must be able to link them correctly. | ||
44 | * | ||
45 | * I don't even know if Apple ever uses the second | ||
46 | * port on the tas3004 though, I don't think their | ||
47 | * i2s controllers can even do it. OTOH, they all | ||
48 | * derive the clocks from common clocks, so it | ||
49 | * might just be possible. The framework allows the | ||
50 | * codec to refine the transfer_info items in the | ||
51 | * usable callback, so we can simply remove the | ||
52 | * rates the second instance is not using when it | ||
53 | * actually is in use. | ||
54 | * Maybe we'll need to make the sound busses have | ||
55 | * a 'clock group id' value so the codec can | ||
56 | * determine if the two outputs can be driven at | ||
57 | * the same time. But that is likely overkill, up | ||
58 | * to the fabric to not link them up incorrectly, | ||
59 | * and up to the hardware designer to not wire | ||
60 | * them up in some weird unusable way. | ||
61 | */ | ||
62 | #include <stddef.h> | ||
63 | #include <linux/i2c.h> | ||
64 | #include <linux/i2c-dev.h> | ||
65 | #include <asm/pmac_low_i2c.h> | ||
66 | #include <asm/prom.h> | ||
67 | #include <linux/delay.h> | ||
68 | #include <linux/module.h> | ||
69 | MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>"); | ||
70 | MODULE_LICENSE("GPL"); | ||
71 | MODULE_DESCRIPTION("tas codec driver for snd-aoa"); | ||
72 | |||
73 | #include "snd-aoa-codec-tas.h" | ||
74 | #include "snd-aoa-codec-tas-gain-table.h" | ||
75 | #include "../aoa.h" | ||
76 | #include "../soundbus/soundbus.h" | ||
77 | |||
78 | |||
79 | #define PFX "snd-aoa-codec-tas: " | ||
80 | |||
81 | struct tas { | ||
82 | struct aoa_codec codec; | ||
83 | struct i2c_client i2c; | ||
84 | u32 muted_l:1, muted_r:1, | ||
85 | controls_created:1; | ||
86 | u8 cached_volume_l, cached_volume_r; | ||
87 | u8 mixer_l[3], mixer_r[3]; | ||
88 | u8 acr; | ||
89 | }; | ||
90 | |||
91 | static struct tas *codec_to_tas(struct aoa_codec *codec) | ||
92 | { | ||
93 | return container_of(codec, struct tas, codec); | ||
94 | } | ||
95 | |||
96 | static inline int tas_write_reg(struct tas *tas, u8 reg, u8 len, u8 *data) | ||
97 | { | ||
98 | if (len == 1) | ||
99 | return i2c_smbus_write_byte_data(&tas->i2c, reg, *data); | ||
100 | else | ||
101 | return i2c_smbus_write_i2c_block_data(&tas->i2c, reg, len, data); | ||
102 | } | ||
103 | |||
104 | static void tas_set_volume(struct tas *tas) | ||
105 | { | ||
106 | u8 block[6]; | ||
107 | int tmp; | ||
108 | u8 left, right; | ||
109 | |||
110 | left = tas->cached_volume_l; | ||
111 | right = tas->cached_volume_r; | ||
112 | |||
113 | if (left > 177) left = 177; | ||
114 | if (right > 177) right = 177; | ||
115 | |||
116 | if (tas->muted_l) left = 0; | ||
117 | if (tas->muted_r) right = 0; | ||
118 | |||
119 | /* analysing the volume and mixer tables shows | ||
120 | * that they are similar enough when we shift | ||
121 | * the mixer table down by 4 bits. The error | ||
122 | * is miniscule, in just one item the error | ||
123 | * is 1, at a value of 0x07f17b (mixer table | ||
124 | * value is 0x07f17a) */ | ||
125 | tmp = tas_gaintable[left]; | ||
126 | block[0] = tmp>>20; | ||
127 | block[1] = tmp>>12; | ||
128 | block[2] = tmp>>4; | ||
129 | tmp = tas_gaintable[right]; | ||
130 | block[3] = tmp>>20; | ||
131 | block[4] = tmp>>12; | ||
132 | block[5] = tmp>>4; | ||
133 | tas_write_reg(tas, TAS_REG_VOL, 6, block); | ||
134 | } | ||
135 | |||
136 | static void tas_set_mixer(struct tas *tas) | ||
137 | { | ||
138 | u8 block[9]; | ||
139 | int tmp, i; | ||
140 | u8 val; | ||
141 | |||
142 | for (i=0;i<3;i++) { | ||
143 | val = tas->mixer_l[i]; | ||
144 | if (val > 177) val = 177; | ||
145 | tmp = tas_gaintable[val]; | ||
146 | block[3*i+0] = tmp>>16; | ||
147 | block[3*i+1] = tmp>>8; | ||
148 | block[3*i+2] = tmp; | ||
149 | } | ||
150 | tas_write_reg(tas, TAS_REG_LMIX, 9, block); | ||
151 | |||
152 | for (i=0;i<3;i++) { | ||
153 | val = tas->mixer_r[i]; | ||
154 | if (val > 177) val = 177; | ||
155 | tmp = tas_gaintable[val]; | ||
156 | block[3*i+0] = tmp>>16; | ||
157 | block[3*i+1] = tmp>>8; | ||
158 | block[3*i+2] = tmp; | ||
159 | } | ||
160 | tas_write_reg(tas, TAS_REG_RMIX, 9, block); | ||
161 | } | ||
162 | |||
163 | /* alsa stuff */ | ||
164 | |||
165 | static int tas_dev_register(struct snd_device *dev) | ||
166 | { | ||
167 | return 0; | ||
168 | } | ||
169 | |||
170 | static struct snd_device_ops ops = { | ||
171 | .dev_register = tas_dev_register, | ||
172 | }; | ||
173 | |||
174 | static int tas_snd_vol_info(struct snd_kcontrol *kcontrol, | ||
175 | struct snd_ctl_elem_info *uinfo) | ||
176 | { | ||
177 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
178 | uinfo->count = 2; | ||
179 | uinfo->value.integer.min = 0; | ||
180 | uinfo->value.integer.max = 177; | ||
181 | return 0; | ||
182 | } | ||
183 | |||
184 | static int tas_snd_vol_get(struct snd_kcontrol *kcontrol, | ||
185 | struct snd_ctl_elem_value *ucontrol) | ||
186 | { | ||
187 | struct tas *tas = snd_kcontrol_chip(kcontrol); | ||
188 | |||
189 | ucontrol->value.integer.value[0] = tas->cached_volume_l; | ||
190 | ucontrol->value.integer.value[1] = tas->cached_volume_r; | ||
191 | return 0; | ||
192 | } | ||
193 | |||
194 | static int tas_snd_vol_put(struct snd_kcontrol *kcontrol, | ||
195 | struct snd_ctl_elem_value *ucontrol) | ||
196 | { | ||
197 | struct tas *tas = snd_kcontrol_chip(kcontrol); | ||
198 | |||
199 | if (tas->cached_volume_l == ucontrol->value.integer.value[0] | ||
200 | && tas->cached_volume_r == ucontrol->value.integer.value[1]) | ||
201 | return 0; | ||
202 | |||
203 | tas->cached_volume_l = ucontrol->value.integer.value[0]; | ||
204 | tas->cached_volume_r = ucontrol->value.integer.value[1]; | ||
205 | tas_set_volume(tas); | ||
206 | return 1; | ||
207 | } | ||
208 | |||
209 | static struct snd_kcontrol_new volume_control = { | ||
210 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
211 | .name = "Master Playback Volume", | ||
212 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, | ||
213 | .info = tas_snd_vol_info, | ||
214 | .get = tas_snd_vol_get, | ||
215 | .put = tas_snd_vol_put, | ||
216 | }; | ||
217 | |||
218 | static int tas_snd_mute_info(struct snd_kcontrol *kcontrol, | ||
219 | struct snd_ctl_elem_info *uinfo) | ||
220 | { | ||
221 | uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; | ||
222 | uinfo->count = 2; | ||
223 | uinfo->value.integer.min = 0; | ||
224 | uinfo->value.integer.max = 1; | ||
225 | return 0; | ||
226 | } | ||
227 | |||
228 | static int tas_snd_mute_get(struct snd_kcontrol *kcontrol, | ||
229 | struct snd_ctl_elem_value *ucontrol) | ||
230 | { | ||
231 | struct tas *tas = snd_kcontrol_chip(kcontrol); | ||
232 | |||
233 | ucontrol->value.integer.value[0] = !tas->muted_l; | ||
234 | ucontrol->value.integer.value[1] = !tas->muted_r; | ||
235 | return 0; | ||
236 | } | ||
237 | |||
238 | static int tas_snd_mute_put(struct snd_kcontrol *kcontrol, | ||
239 | struct snd_ctl_elem_value *ucontrol) | ||
240 | { | ||
241 | struct tas *tas = snd_kcontrol_chip(kcontrol); | ||
242 | |||
243 | if (tas->muted_l == !ucontrol->value.integer.value[0] | ||
244 | && tas->muted_r == !ucontrol->value.integer.value[1]) | ||
245 | return 0; | ||
246 | |||
247 | tas->muted_l = !ucontrol->value.integer.value[0]; | ||
248 | tas->muted_r = !ucontrol->value.integer.value[1]; | ||
249 | tas_set_volume(tas); | ||
250 | return 1; | ||
251 | } | ||
252 | |||
253 | static struct snd_kcontrol_new mute_control = { | ||
254 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
255 | .name = "Master Playback Switch", | ||
256 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, | ||
257 | .info = tas_snd_mute_info, | ||
258 | .get = tas_snd_mute_get, | ||
259 | .put = tas_snd_mute_put, | ||
260 | }; | ||
261 | |||
262 | static int tas_snd_mixer_info(struct snd_kcontrol *kcontrol, | ||
263 | struct snd_ctl_elem_info *uinfo) | ||
264 | { | ||
265 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
266 | uinfo->count = 2; | ||
267 | uinfo->value.integer.min = 0; | ||
268 | uinfo->value.integer.max = 177; | ||
269 | return 0; | ||
270 | } | ||
271 | |||
272 | static int tas_snd_mixer_get(struct snd_kcontrol *kcontrol, | ||
273 | struct snd_ctl_elem_value *ucontrol) | ||
274 | { | ||
275 | struct tas *tas = snd_kcontrol_chip(kcontrol); | ||
276 | int idx = kcontrol->private_value; | ||
277 | |||
278 | ucontrol->value.integer.value[0] = tas->mixer_l[idx]; | ||
279 | ucontrol->value.integer.value[1] = tas->mixer_r[idx]; | ||
280 | |||
281 | return 0; | ||
282 | } | ||
283 | |||
284 | static int tas_snd_mixer_put(struct snd_kcontrol *kcontrol, | ||
285 | struct snd_ctl_elem_value *ucontrol) | ||
286 | { | ||
287 | struct tas *tas = snd_kcontrol_chip(kcontrol); | ||
288 | int idx = kcontrol->private_value; | ||
289 | |||
290 | if (tas->mixer_l[idx] == ucontrol->value.integer.value[0] | ||
291 | && tas->mixer_r[idx] == ucontrol->value.integer.value[1]) | ||
292 | return 0; | ||
293 | |||
294 | tas->mixer_l[idx] = ucontrol->value.integer.value[0]; | ||
295 | tas->mixer_r[idx] = ucontrol->value.integer.value[1]; | ||
296 | |||
297 | tas_set_mixer(tas); | ||
298 | return 1; | ||
299 | } | ||
300 | |||
301 | #define MIXER_CONTROL(n,descr,idx) \ | ||
302 | static struct snd_kcontrol_new n##_control = { \ | ||
303 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ | ||
304 | .name = descr " Playback Volume", \ | ||
305 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \ | ||
306 | .info = tas_snd_mixer_info, \ | ||
307 | .get = tas_snd_mixer_get, \ | ||
308 | .put = tas_snd_mixer_put, \ | ||
309 | .private_value = idx, \ | ||
310 | } | ||
311 | |||
312 | MIXER_CONTROL(pcm1, "PCM1", 0); | ||
313 | MIXER_CONTROL(monitor, "Monitor", 2); | ||
314 | |||
315 | static int tas_snd_capture_source_info(struct snd_kcontrol *kcontrol, | ||
316 | struct snd_ctl_elem_info *uinfo) | ||
317 | { | ||
318 | static char *texts[] = { "Line-In", "Microphone" }; | ||
319 | |||
320 | uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; | ||
321 | uinfo->count = 1; | ||
322 | uinfo->value.enumerated.items = 2; | ||
323 | if (uinfo->value.enumerated.item > 1) | ||
324 | uinfo->value.enumerated.item = 1; | ||
325 | strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); | ||
326 | return 0; | ||
327 | } | ||
328 | |||
329 | static int tas_snd_capture_source_get(struct snd_kcontrol *kcontrol, | ||
330 | struct snd_ctl_elem_value *ucontrol) | ||
331 | { | ||
332 | struct tas *tas = snd_kcontrol_chip(kcontrol); | ||
333 | |||
334 | ucontrol->value.enumerated.item[0] = !!(tas->acr & TAS_ACR_INPUT_B); | ||
335 | return 0; | ||
336 | } | ||
337 | |||
338 | static int tas_snd_capture_source_put(struct snd_kcontrol *kcontrol, | ||
339 | struct snd_ctl_elem_value *ucontrol) | ||
340 | { | ||
341 | struct tas *tas = snd_kcontrol_chip(kcontrol); | ||
342 | int oldacr = tas->acr; | ||
343 | |||
344 | tas->acr &= ~TAS_ACR_INPUT_B; | ||
345 | if (ucontrol->value.enumerated.item[0]) | ||
346 | tas->acr |= TAS_ACR_INPUT_B; | ||
347 | if (oldacr == tas->acr) | ||
348 | return 0; | ||
349 | tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr); | ||
350 | return 1; | ||
351 | } | ||
352 | |||
353 | static struct snd_kcontrol_new capture_source_control = { | ||
354 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
355 | /* If we name this 'Input Source', it properly shows up in | ||
356 | * alsamixer as a selection, * but it's shown under the | ||
357 | * 'Playback' category. | ||
358 | * If I name it 'Capture Source', it shows up in strange | ||
359 | * ways (two bools of which one can be selected at a | ||
360 | * time) but at least it's shown in the 'Capture' | ||
361 | * category. | ||
362 | * I was told that this was due to backward compatibility, | ||
363 | * but I don't understand then why the mangling is *not* | ||
364 | * done when I name it "Input Source"..... | ||
365 | */ | ||
366 | .name = "Capture Source", | ||
367 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, | ||
368 | .info = tas_snd_capture_source_info, | ||
369 | .get = tas_snd_capture_source_get, | ||
370 | .put = tas_snd_capture_source_put, | ||
371 | }; | ||
372 | |||
373 | |||
374 | static struct transfer_info tas_transfers[] = { | ||
375 | { | ||
376 | /* input */ | ||
377 | .formats = SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_S16_BE | | ||
378 | SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S24_BE, | ||
379 | .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, | ||
380 | .transfer_in = 1, | ||
381 | }, | ||
382 | { | ||
383 | /* output */ | ||
384 | .formats = SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_S16_BE | | ||
385 | SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S24_BE, | ||
386 | .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, | ||
387 | .transfer_in = 0, | ||
388 | }, | ||
389 | {} | ||
390 | }; | ||
391 | |||
392 | static int tas_usable(struct codec_info_item *cii, | ||
393 | struct transfer_info *ti, | ||
394 | struct transfer_info *out) | ||
395 | { | ||
396 | return 1; | ||
397 | } | ||
398 | |||
399 | static int tas_reset_init(struct tas *tas) | ||
400 | { | ||
401 | u8 tmp; | ||
402 | tas->codec.gpio->methods->set_hw_reset(tas->codec.gpio, 0); | ||
403 | msleep(1); | ||
404 | tas->codec.gpio->methods->set_hw_reset(tas->codec.gpio, 1); | ||
405 | msleep(1); | ||
406 | tas->codec.gpio->methods->set_hw_reset(tas->codec.gpio, 0); | ||
407 | msleep(1); | ||
408 | |||
409 | tas->acr &= ~TAS_ACR_ANALOG_PDOWN; | ||
410 | tas->acr |= TAS_ACR_B_MONAUREAL | TAS_ACR_B_MON_SEL_RIGHT; | ||
411 | if (tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr)) | ||
412 | return -ENODEV; | ||
413 | |||
414 | tmp = TAS_MCS_SCLK64 | TAS_MCS_SPORT_MODE_I2S | TAS_MCS_SPORT_WL_24BIT; | ||
415 | if (tas_write_reg(tas, TAS_REG_MCS, 1, &tmp)) | ||
416 | return -ENODEV; | ||
417 | |||
418 | tmp = 0; | ||
419 | if (tas_write_reg(tas, TAS_REG_MCS2, 1, &tmp)) | ||
420 | return -ENODEV; | ||
421 | |||
422 | return 0; | ||
423 | } | ||
424 | |||
425 | /* we are controlled via i2c and assume that is always up | ||
426 | * If that wasn't the case, we'd have to suspend once | ||
427 | * our i2c device is suspended, and then take note of that! */ | ||
428 | static int tas_suspend(struct tas *tas) | ||
429 | { | ||
430 | tas->acr |= TAS_ACR_ANALOG_PDOWN; | ||
431 | tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr); | ||
432 | return 0; | ||
433 | } | ||
434 | |||
435 | static int tas_resume(struct tas *tas) | ||
436 | { | ||
437 | /* reset codec */ | ||
438 | tas_reset_init(tas); | ||
439 | tas_set_volume(tas); | ||
440 | tas_set_mixer(tas); | ||
441 | return 0; | ||
442 | } | ||
443 | |||
444 | #ifdef CONFIG_PM | ||
445 | static int _tas_suspend(struct codec_info_item *cii, pm_message_t state) | ||
446 | { | ||
447 | return tas_suspend(cii->codec_data); | ||
448 | } | ||
449 | |||
450 | static int _tas_resume(struct codec_info_item *cii) | ||
451 | { | ||
452 | return tas_resume(cii->codec_data); | ||
453 | } | ||
454 | #endif | ||
455 | |||
456 | static struct codec_info tas_codec_info = { | ||
457 | .transfers = tas_transfers, | ||
458 | /* in theory, we can drive it at 512 too... | ||
459 | * but so far the framework doesn't allow | ||
460 | * for that and I don't see much point in it. */ | ||
461 | .sysclock_factor = 256, | ||
462 | /* same here, could be 32 for just one 16 bit format */ | ||
463 | .bus_factor = 64, | ||
464 | .owner = THIS_MODULE, | ||
465 | .usable = tas_usable, | ||
466 | #ifdef CONFIG_PM | ||
467 | .suspend = _tas_suspend, | ||
468 | .resume = _tas_resume, | ||
469 | #endif | ||
470 | }; | ||
471 | |||
472 | static int tas_init_codec(struct aoa_codec *codec) | ||
473 | { | ||
474 | struct tas *tas = codec_to_tas(codec); | ||
475 | int err; | ||
476 | |||
477 | if (!tas->codec.gpio || !tas->codec.gpio->methods) { | ||
478 | printk(KERN_ERR PFX "gpios not assigned!!\n"); | ||
479 | return -EINVAL; | ||
480 | } | ||
481 | |||
482 | if (tas_reset_init(tas)) { | ||
483 | printk(KERN_ERR PFX "tas failed to initialise\n"); | ||
484 | return -ENXIO; | ||
485 | } | ||
486 | |||
487 | if (tas->codec.soundbus_dev->attach_codec(tas->codec.soundbus_dev, | ||
488 | aoa_get_card(), | ||
489 | &tas_codec_info, tas)) { | ||
490 | printk(KERN_ERR PFX "error attaching tas to soundbus\n"); | ||
491 | return -ENODEV; | ||
492 | } | ||
493 | |||
494 | if (aoa_snd_device_new(SNDRV_DEV_LOWLEVEL, tas, &ops)) { | ||
495 | printk(KERN_ERR PFX "failed to create tas snd device!\n"); | ||
496 | return -ENODEV; | ||
497 | } | ||
498 | err = aoa_snd_ctl_add(snd_ctl_new1(&volume_control, tas)); | ||
499 | if (err) | ||
500 | goto error; | ||
501 | |||
502 | err = aoa_snd_ctl_add(snd_ctl_new1(&mute_control, tas)); | ||
503 | if (err) | ||
504 | goto error; | ||
505 | |||
506 | err = aoa_snd_ctl_add(snd_ctl_new1(&pcm1_control, tas)); | ||
507 | if (err) | ||
508 | goto error; | ||
509 | |||
510 | err = aoa_snd_ctl_add(snd_ctl_new1(&monitor_control, tas)); | ||
511 | if (err) | ||
512 | goto error; | ||
513 | |||
514 | err = aoa_snd_ctl_add(snd_ctl_new1(&capture_source_control, tas)); | ||
515 | if (err) | ||
516 | goto error; | ||
517 | |||
518 | return 0; | ||
519 | error: | ||
520 | tas->codec.soundbus_dev->detach_codec(tas->codec.soundbus_dev, tas); | ||
521 | snd_device_free(aoa_get_card(), tas); | ||
522 | return err; | ||
523 | } | ||
524 | |||
525 | static void tas_exit_codec(struct aoa_codec *codec) | ||
526 | { | ||
527 | struct tas *tas = codec_to_tas(codec); | ||
528 | |||
529 | if (!tas->codec.soundbus_dev) | ||
530 | return; | ||
531 | tas->codec.soundbus_dev->detach_codec(tas->codec.soundbus_dev, tas); | ||
532 | } | ||
533 | |||
534 | |||
535 | static struct i2c_driver tas_driver; | ||
536 | |||
537 | static int tas_create(struct i2c_adapter *adapter, | ||
538 | struct device_node *node, | ||
539 | int addr) | ||
540 | { | ||
541 | struct tas *tas; | ||
542 | |||
543 | tas = kzalloc(sizeof(struct tas), GFP_KERNEL); | ||
544 | |||
545 | if (!tas) | ||
546 | return -ENOMEM; | ||
547 | |||
548 | tas->i2c.driver = &tas_driver; | ||
549 | tas->i2c.adapter = adapter; | ||
550 | tas->i2c.addr = addr; | ||
551 | strlcpy(tas->i2c.name, "tas audio codec", I2C_NAME_SIZE-1); | ||
552 | |||
553 | if (i2c_attach_client(&tas->i2c)) { | ||
554 | printk(KERN_ERR PFX "failed to attach to i2c\n"); | ||
555 | goto fail; | ||
556 | } | ||
557 | |||
558 | strlcpy(tas->codec.name, "tas", MAX_CODEC_NAME_LEN-1); | ||
559 | tas->codec.owner = THIS_MODULE; | ||
560 | tas->codec.init = tas_init_codec; | ||
561 | tas->codec.exit = tas_exit_codec; | ||
562 | tas->codec.node = of_node_get(node); | ||
563 | |||
564 | if (aoa_codec_register(&tas->codec)) { | ||
565 | goto detach; | ||
566 | } | ||
567 | printk(KERN_DEBUG "snd-aoa-codec-tas: created and attached tas instance\n"); | ||
568 | return 0; | ||
569 | detach: | ||
570 | i2c_detach_client(&tas->i2c); | ||
571 | fail: | ||
572 | kfree(tas); | ||
573 | return -EINVAL; | ||
574 | } | ||
575 | |||
576 | static int tas_i2c_attach(struct i2c_adapter *adapter) | ||
577 | { | ||
578 | struct device_node *busnode, *dev = NULL; | ||
579 | struct pmac_i2c_bus *bus; | ||
580 | |||
581 | bus = pmac_i2c_adapter_to_bus(adapter); | ||
582 | if (bus == NULL) | ||
583 | return -ENODEV; | ||
584 | busnode = pmac_i2c_get_bus_node(bus); | ||
585 | |||
586 | while ((dev = of_get_next_child(busnode, dev)) != NULL) { | ||
587 | if (device_is_compatible(dev, "tas3004")) { | ||
588 | u32 *addr; | ||
589 | printk(KERN_DEBUG PFX "found tas3004\n"); | ||
590 | addr = (u32 *) get_property(dev, "reg", NULL); | ||
591 | if (!addr) | ||
592 | continue; | ||
593 | return tas_create(adapter, dev, ((*addr) >> 1) & 0x7f); | ||
594 | } | ||
595 | /* older machines have no 'codec' node with a 'compatible' | ||
596 | * property that says 'tas3004', they just have a 'deq' | ||
597 | * node without any such property... */ | ||
598 | if (strcmp(dev->name, "deq") == 0) { | ||
599 | u32 *_addr, addr; | ||
600 | printk(KERN_DEBUG PFX "found 'deq' node\n"); | ||
601 | _addr = (u32 *) get_property(dev, "i2c-address", NULL); | ||
602 | if (!_addr) | ||
603 | continue; | ||
604 | addr = ((*_addr) >> 1) & 0x7f; | ||
605 | /* now, if the address doesn't match any of the two | ||
606 | * that a tas3004 can have, we cannot handle this. | ||
607 | * I doubt it ever happens but hey. */ | ||
608 | if (addr != 0x34 && addr != 0x35) | ||
609 | continue; | ||
610 | return tas_create(adapter, dev, addr); | ||
611 | } | ||
612 | } | ||
613 | return -ENODEV; | ||
614 | } | ||
615 | |||
616 | static int tas_i2c_detach(struct i2c_client *client) | ||
617 | { | ||
618 | struct tas *tas = container_of(client, struct tas, i2c); | ||
619 | int err; | ||
620 | u8 tmp = TAS_ACR_ANALOG_PDOWN; | ||
621 | |||
622 | if ((err = i2c_detach_client(client))) | ||
623 | return err; | ||
624 | aoa_codec_unregister(&tas->codec); | ||
625 | of_node_put(tas->codec.node); | ||
626 | |||
627 | /* power down codec chip */ | ||
628 | tas_write_reg(tas, TAS_REG_ACR, 1, &tmp); | ||
629 | |||
630 | kfree(tas); | ||
631 | return 0; | ||
632 | } | ||
633 | |||
634 | static struct i2c_driver tas_driver = { | ||
635 | .driver = { | ||
636 | .name = "aoa_codec_tas", | ||
637 | .owner = THIS_MODULE, | ||
638 | }, | ||
639 | .attach_adapter = tas_i2c_attach, | ||
640 | .detach_client = tas_i2c_detach, | ||
641 | }; | ||
642 | |||
643 | static int __init tas_init(void) | ||
644 | { | ||
645 | return i2c_add_driver(&tas_driver); | ||
646 | } | ||
647 | |||
648 | static void __exit tas_exit(void) | ||
649 | { | ||
650 | i2c_del_driver(&tas_driver); | ||
651 | } | ||
652 | |||
653 | module_init(tas_init); | ||
654 | module_exit(tas_exit); | ||
diff --git a/sound/aoa/codecs/snd-aoa-codec-tas.h b/sound/aoa/codecs/snd-aoa-codec-tas.h new file mode 100644 index 000000000000..daf81f45d83a --- /dev/null +++ b/sound/aoa/codecs/snd-aoa-codec-tas.h | |||
@@ -0,0 +1,47 @@ | |||
1 | /* | ||
2 | * Apple Onboard Audio driver for tas codec (header) | ||
3 | * | ||
4 | * Copyright 2006 Johannes Berg <johannes@sipsolutions.net> | ||
5 | * | ||
6 | * GPL v2, can be found in COPYING. | ||
7 | */ | ||
8 | #ifndef __SND_AOA_CODECTASH | ||
9 | #define __SND_AOA_CODECTASH | ||
10 | |||
11 | #define TAS_REG_MCS 0x01 /* main control */ | ||
12 | # define TAS_MCS_FASTLOAD (1<<7) | ||
13 | # define TAS_MCS_SCLK64 (1<<6) | ||
14 | # define TAS_MCS_SPORT_MODE_MASK (3<<4) | ||
15 | # define TAS_MCS_SPORT_MODE_I2S (2<<4) | ||
16 | # define TAS_MCS_SPORT_MODE_RJ (1<<4) | ||
17 | # define TAS_MCS_SPORT_MODE_LJ (0<<4) | ||
18 | # define TAS_MCS_SPORT_WL_MASK (3<<0) | ||
19 | # define TAS_MCS_SPORT_WL_16BIT (0<<0) | ||
20 | # define TAS_MCS_SPORT_WL_18BIT (1<<0) | ||
21 | # define TAS_MCS_SPORT_WL_20BIT (2<<0) | ||
22 | # define TAS_MCS_SPORT_WL_24BIT (3<<0) | ||
23 | |||
24 | #define TAS_REG_DRC 0x02 | ||
25 | #define TAS_REG_VOL 0x04 | ||
26 | #define TAS_REG_TREBLE 0x05 | ||
27 | #define TAS_REG_BASS 0x06 | ||
28 | #define TAS_REG_LMIX 0x07 | ||
29 | #define TAS_REG_RMIX 0x08 | ||
30 | |||
31 | #define TAS_REG_ACR 0x40 /* analog control */ | ||
32 | # define TAS_ACR_B_MONAUREAL (1<<7) | ||
33 | # define TAS_ACR_B_MON_SEL_RIGHT (1<<6) | ||
34 | # define TAS_ACR_DEEMPH_MASK (3<<2) | ||
35 | # define TAS_ACR_DEEMPH_OFF (0<<2) | ||
36 | # define TAS_ACR_DEEMPH_48KHz (1<<2) | ||
37 | # define TAS_ACR_DEEMPH_44KHz (2<<2) | ||
38 | # define TAS_ACR_INPUT_B (1<<1) | ||
39 | # define TAS_ACR_ANALOG_PDOWN (1<<0) | ||
40 | |||
41 | #define TAS_REG_MCS2 0x43 /* main control 2 */ | ||
42 | # define TAS_MCS2_ALLPASS (1<<1) | ||
43 | |||
44 | #define TAS_REG_LEFT_BIQUAD6 0x10 | ||
45 | #define TAS_REG_RIGHT_BIQUAD6 0x19 | ||
46 | |||
47 | #endif /* __SND_AOA_CODECTASH */ | ||
diff --git a/sound/aoa/codecs/snd-aoa-codec-toonie.c b/sound/aoa/codecs/snd-aoa-codec-toonie.c new file mode 100644 index 000000000000..bcc555647e79 --- /dev/null +++ b/sound/aoa/codecs/snd-aoa-codec-toonie.c | |||
@@ -0,0 +1,141 @@ | |||
1 | /* | ||
2 | * Apple Onboard Audio driver for Toonie codec | ||
3 | * | ||
4 | * Copyright 2006 Johannes Berg <johannes@sipsolutions.net> | ||
5 | * | ||
6 | * GPL v2, can be found in COPYING. | ||
7 | * | ||
8 | * | ||
9 | * This is a driver for the toonie codec chip. This chip is present | ||
10 | * on the Mac Mini and is nothing but a DAC. | ||
11 | */ | ||
12 | #include <linux/delay.h> | ||
13 | #include <linux/module.h> | ||
14 | MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>"); | ||
15 | MODULE_LICENSE("GPL"); | ||
16 | MODULE_DESCRIPTION("toonie codec driver for snd-aoa"); | ||
17 | |||
18 | #include "../aoa.h" | ||
19 | #include "../soundbus/soundbus.h" | ||
20 | |||
21 | |||
22 | #define PFX "snd-aoa-codec-toonie: " | ||
23 | |||
24 | struct toonie { | ||
25 | struct aoa_codec codec; | ||
26 | }; | ||
27 | #define codec_to_toonie(c) container_of(c, struct toonie, codec) | ||
28 | |||
29 | static int toonie_dev_register(struct snd_device *dev) | ||
30 | { | ||
31 | return 0; | ||
32 | } | ||
33 | |||
34 | static struct snd_device_ops ops = { | ||
35 | .dev_register = toonie_dev_register, | ||
36 | }; | ||
37 | |||
38 | static struct transfer_info toonie_transfers[] = { | ||
39 | /* This thing *only* has analog output, | ||
40 | * the rates are taken from Info.plist | ||
41 | * from Darwin. */ | ||
42 | { | ||
43 | .formats = SNDRV_PCM_FMTBIT_S16_BE | | ||
44 | SNDRV_PCM_FMTBIT_S24_BE, | ||
45 | .rates = SNDRV_PCM_RATE_32000 | | ||
46 | SNDRV_PCM_RATE_44100 | | ||
47 | SNDRV_PCM_RATE_48000 | | ||
48 | SNDRV_PCM_RATE_88200 | | ||
49 | SNDRV_PCM_RATE_96000, | ||
50 | }, | ||
51 | {} | ||
52 | }; | ||
53 | |||
54 | #ifdef CONFIG_PM | ||
55 | static int toonie_suspend(struct codec_info_item *cii, pm_message_t state) | ||
56 | { | ||
57 | /* can we turn it off somehow? */ | ||
58 | return 0; | ||
59 | } | ||
60 | |||
61 | static int toonie_resume(struct codec_info_item *cii) | ||
62 | { | ||
63 | return 0; | ||
64 | } | ||
65 | #endif /* CONFIG_PM */ | ||
66 | |||
67 | static struct codec_info toonie_codec_info = { | ||
68 | .transfers = toonie_transfers, | ||
69 | .sysclock_factor = 256, | ||
70 | .bus_factor = 64, | ||
71 | .owner = THIS_MODULE, | ||
72 | #ifdef CONFIG_PM | ||
73 | .suspend = toonie_suspend, | ||
74 | .resume = toonie_resume, | ||
75 | #endif | ||
76 | }; | ||
77 | |||
78 | static int toonie_init_codec(struct aoa_codec *codec) | ||
79 | { | ||
80 | struct toonie *toonie = codec_to_toonie(codec); | ||
81 | |||
82 | if (aoa_snd_device_new(SNDRV_DEV_LOWLEVEL, toonie, &ops)) { | ||
83 | printk(KERN_ERR PFX "failed to create toonie snd device!\n"); | ||
84 | return -ENODEV; | ||
85 | } | ||
86 | |||
87 | /* nothing connected? what a joke! */ | ||
88 | if (toonie->codec.connected != 1) | ||
89 | return -ENOTCONN; | ||
90 | |||
91 | if (toonie->codec.soundbus_dev->attach_codec(toonie->codec.soundbus_dev, | ||
92 | aoa_get_card(), | ||
93 | &toonie_codec_info, toonie)) { | ||
94 | printk(KERN_ERR PFX "error creating toonie pcm\n"); | ||
95 | return -ENODEV; | ||
96 | } | ||
97 | |||
98 | return 0; | ||
99 | } | ||
100 | |||
101 | static void toonie_exit_codec(struct aoa_codec *codec) | ||
102 | { | ||
103 | struct toonie *toonie = codec_to_toonie(codec); | ||
104 | |||
105 | if (!toonie->codec.soundbus_dev) { | ||
106 | printk(KERN_ERR PFX "toonie_exit_codec called without soundbus_dev!\n"); | ||
107 | return; | ||
108 | } | ||
109 | toonie->codec.soundbus_dev->detach_codec(toonie->codec.soundbus_dev, toonie); | ||
110 | } | ||
111 | |||
112 | static struct toonie *toonie; | ||
113 | |||
114 | static int __init toonie_init(void) | ||
115 | { | ||
116 | toonie = kzalloc(sizeof(struct toonie), GFP_KERNEL); | ||
117 | |||
118 | if (!toonie) | ||
119 | return -ENOMEM; | ||
120 | |||
121 | strlcpy(toonie->codec.name, "toonie", sizeof(toonie->codec.name)); | ||
122 | toonie->codec.owner = THIS_MODULE; | ||
123 | toonie->codec.init = toonie_init_codec; | ||
124 | toonie->codec.exit = toonie_exit_codec; | ||
125 | |||
126 | if (aoa_codec_register(&toonie->codec)) { | ||
127 | kfree(toonie); | ||
128 | return -EINVAL; | ||
129 | } | ||
130 | |||
131 | return 0; | ||
132 | } | ||
133 | |||
134 | static void __exit toonie_exit(void) | ||
135 | { | ||
136 | aoa_codec_unregister(&toonie->codec); | ||
137 | kfree(toonie); | ||
138 | } | ||
139 | |||
140 | module_init(toonie_init); | ||
141 | module_exit(toonie_exit); | ||
diff --git a/sound/aoa/core/Makefile b/sound/aoa/core/Makefile new file mode 100644 index 000000000000..62dc7287f663 --- /dev/null +++ b/sound/aoa/core/Makefile | |||
@@ -0,0 +1,5 @@ | |||
1 | obj-$(CONFIG_SND_AOA) += snd-aoa.o | ||
2 | snd-aoa-objs := snd-aoa-core.o \ | ||
3 | snd-aoa-alsa.o \ | ||
4 | snd-aoa-gpio-pmf.o \ | ||
5 | snd-aoa-gpio-feature.o | ||
diff --git a/sound/aoa/core/snd-aoa-alsa.c b/sound/aoa/core/snd-aoa-alsa.c new file mode 100644 index 000000000000..b42fdea77ed0 --- /dev/null +++ b/sound/aoa/core/snd-aoa-alsa.c | |||
@@ -0,0 +1,98 @@ | |||
1 | /* | ||
2 | * Apple Onboard Audio Alsa helpers | ||
3 | * | ||
4 | * Copyright 2006 Johannes Berg <johannes@sipsolutions.net> | ||
5 | * | ||
6 | * GPL v2, can be found in COPYING. | ||
7 | */ | ||
8 | #include <linux/module.h> | ||
9 | #include "snd-aoa-alsa.h" | ||
10 | |||
11 | static int index = -1; | ||
12 | module_param(index, int, 0444); | ||
13 | MODULE_PARM_DESC(index, "index for AOA sound card."); | ||
14 | |||
15 | static struct aoa_card *aoa_card; | ||
16 | |||
17 | int aoa_alsa_init(char *name, struct module *mod) | ||
18 | { | ||
19 | struct snd_card *alsa_card; | ||
20 | int err; | ||
21 | |||
22 | if (aoa_card) | ||
23 | /* cannot be EEXIST due to usage in aoa_fabric_register */ | ||
24 | return -EBUSY; | ||
25 | |||
26 | alsa_card = snd_card_new(index, name, mod, sizeof(struct aoa_card)); | ||
27 | if (!alsa_card) | ||
28 | return -ENOMEM; | ||
29 | aoa_card = alsa_card->private_data; | ||
30 | aoa_card->alsa_card = alsa_card; | ||
31 | strlcpy(alsa_card->driver, "AppleOnbdAudio", sizeof(alsa_card->driver)); | ||
32 | strlcpy(alsa_card->shortname, name, sizeof(alsa_card->shortname)); | ||
33 | strlcpy(alsa_card->longname, name, sizeof(alsa_card->longname)); | ||
34 | strlcpy(alsa_card->mixername, name, sizeof(alsa_card->mixername)); | ||
35 | err = snd_card_register(aoa_card->alsa_card); | ||
36 | if (err < 0) { | ||
37 | printk(KERN_ERR "snd-aoa: couldn't register alsa card\n"); | ||
38 | snd_card_free(aoa_card->alsa_card); | ||
39 | aoa_card = NULL; | ||
40 | return err; | ||
41 | } | ||
42 | return 0; | ||
43 | } | ||
44 | |||
45 | struct snd_card *aoa_get_card(void) | ||
46 | { | ||
47 | if (aoa_card) | ||
48 | return aoa_card->alsa_card; | ||
49 | return NULL; | ||
50 | } | ||
51 | EXPORT_SYMBOL_GPL(aoa_get_card); | ||
52 | |||
53 | void aoa_alsa_cleanup(void) | ||
54 | { | ||
55 | if (aoa_card) { | ||
56 | snd_card_free(aoa_card->alsa_card); | ||
57 | aoa_card = NULL; | ||
58 | } | ||
59 | } | ||
60 | |||
61 | int aoa_snd_device_new(snd_device_type_t type, | ||
62 | void * device_data, struct snd_device_ops * ops) | ||
63 | { | ||
64 | struct snd_card *card = aoa_get_card(); | ||
65 | int err; | ||
66 | |||
67 | if (!card) return -ENOMEM; | ||
68 | |||
69 | err = snd_device_new(card, type, device_data, ops); | ||
70 | if (err) { | ||
71 | printk(KERN_ERR "snd-aoa: failed to create snd device (%d)\n", err); | ||
72 | return err; | ||
73 | } | ||
74 | err = snd_device_register(card, device_data); | ||
75 | if (err) { | ||
76 | printk(KERN_ERR "snd-aoa: failed to register " | ||
77 | "snd device (%d)\n", err); | ||
78 | printk(KERN_ERR "snd-aoa: have you forgotten the " | ||
79 | "dev_register callback?\n"); | ||
80 | snd_device_free(card, device_data); | ||
81 | } | ||
82 | return err; | ||
83 | } | ||
84 | EXPORT_SYMBOL_GPL(aoa_snd_device_new); | ||
85 | |||
86 | int aoa_snd_ctl_add(struct snd_kcontrol* control) | ||
87 | { | ||
88 | int err; | ||
89 | |||
90 | if (!aoa_card) return -ENODEV; | ||
91 | |||
92 | err = snd_ctl_add(aoa_card->alsa_card, control); | ||
93 | if (err) | ||
94 | printk(KERN_ERR "snd-aoa: failed to add alsa control (%d)\n", | ||
95 | err); | ||
96 | return err; | ||
97 | } | ||
98 | EXPORT_SYMBOL_GPL(aoa_snd_ctl_add); | ||
diff --git a/sound/aoa/core/snd-aoa-alsa.h b/sound/aoa/core/snd-aoa-alsa.h new file mode 100644 index 000000000000..660d2f1793bb --- /dev/null +++ b/sound/aoa/core/snd-aoa-alsa.h | |||
@@ -0,0 +1,16 @@ | |||
1 | /* | ||
2 | * Apple Onboard Audio Alsa private helpers | ||
3 | * | ||
4 | * Copyright 2006 Johannes Berg <johannes@sipsolutions.net> | ||
5 | * | ||
6 | * GPL v2, can be found in COPYING. | ||
7 | */ | ||
8 | |||
9 | #ifndef __SND_AOA_ALSA_H | ||
10 | #define __SND_AOA_ALSA_H | ||
11 | #include "../aoa.h" | ||
12 | |||
13 | extern int aoa_alsa_init(char *name, struct module *mod); | ||
14 | extern void aoa_alsa_cleanup(void); | ||
15 | |||
16 | #endif /* __SND_AOA_ALSA_H */ | ||
diff --git a/sound/aoa/core/snd-aoa-core.c b/sound/aoa/core/snd-aoa-core.c new file mode 100644 index 000000000000..ecd2d8263f2d --- /dev/null +++ b/sound/aoa/core/snd-aoa-core.c | |||
@@ -0,0 +1,162 @@ | |||
1 | /* | ||
2 | * Apple Onboard Audio driver core | ||
3 | * | ||
4 | * Copyright 2006 Johannes Berg <johannes@sipsolutions.net> | ||
5 | * | ||
6 | * GPL v2, can be found in COPYING. | ||
7 | */ | ||
8 | |||
9 | #include <linux/init.h> | ||
10 | #include <linux/module.h> | ||
11 | #include <linux/list.h> | ||
12 | #include "../aoa.h" | ||
13 | #include "snd-aoa-alsa.h" | ||
14 | |||
15 | MODULE_DESCRIPTION("Apple Onboard Audio Sound Driver"); | ||
16 | MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>"); | ||
17 | MODULE_LICENSE("GPL"); | ||
18 | |||
19 | /* We allow only one fabric. This simplifies things, | ||
20 | * and more don't really make that much sense */ | ||
21 | static struct aoa_fabric *fabric; | ||
22 | static LIST_HEAD(codec_list); | ||
23 | |||
24 | static int attach_codec_to_fabric(struct aoa_codec *c) | ||
25 | { | ||
26 | int err; | ||
27 | |||
28 | if (!try_module_get(c->owner)) | ||
29 | return -EBUSY; | ||
30 | /* found_codec has to be assigned */ | ||
31 | err = -ENOENT; | ||
32 | if (fabric->found_codec) | ||
33 | err = fabric->found_codec(c); | ||
34 | if (err) { | ||
35 | module_put(c->owner); | ||
36 | printk(KERN_ERR "snd-aoa: fabric didn't like codec %s\n", | ||
37 | c->name); | ||
38 | return err; | ||
39 | } | ||
40 | c->fabric = fabric; | ||
41 | |||
42 | err = 0; | ||
43 | if (c->init) | ||
44 | err = c->init(c); | ||
45 | if (err) { | ||
46 | printk(KERN_ERR "snd-aoa: codec %s didn't init\n", c->name); | ||
47 | c->fabric = NULL; | ||
48 | if (fabric->remove_codec) | ||
49 | fabric->remove_codec(c); | ||
50 | module_put(c->owner); | ||
51 | return err; | ||
52 | } | ||
53 | if (fabric->attached_codec) | ||
54 | fabric->attached_codec(c); | ||
55 | return 0; | ||
56 | } | ||
57 | |||
58 | int aoa_codec_register(struct aoa_codec *codec) | ||
59 | { | ||
60 | int err = 0; | ||
61 | |||
62 | /* if there's a fabric already, we can tell if we | ||
63 | * will want to have this codec, so propagate error | ||
64 | * through. Otherwise, this will happen later... */ | ||
65 | if (fabric) | ||
66 | err = attach_codec_to_fabric(codec); | ||
67 | if (!err) | ||
68 | list_add(&codec->list, &codec_list); | ||
69 | return err; | ||
70 | } | ||
71 | EXPORT_SYMBOL_GPL(aoa_codec_register); | ||
72 | |||
73 | void aoa_codec_unregister(struct aoa_codec *codec) | ||
74 | { | ||
75 | list_del(&codec->list); | ||
76 | if (codec->fabric && codec->exit) | ||
77 | codec->exit(codec); | ||
78 | if (fabric && fabric->remove_codec) | ||
79 | fabric->remove_codec(codec); | ||
80 | codec->fabric = NULL; | ||
81 | module_put(codec->owner); | ||
82 | } | ||
83 | EXPORT_SYMBOL_GPL(aoa_codec_unregister); | ||
84 | |||
85 | int aoa_fabric_register(struct aoa_fabric *new_fabric) | ||
86 | { | ||
87 | struct aoa_codec *c; | ||
88 | int err; | ||
89 | |||
90 | /* allow querying for presence of fabric | ||
91 | * (i.e. do this test first!) */ | ||
92 | if (new_fabric == fabric) { | ||
93 | err = -EALREADY; | ||
94 | goto attach; | ||
95 | } | ||
96 | if (fabric) | ||
97 | return -EEXIST; | ||
98 | if (!new_fabric) | ||
99 | return -EINVAL; | ||
100 | |||
101 | err = aoa_alsa_init(new_fabric->name, new_fabric->owner); | ||
102 | if (err) | ||
103 | return err; | ||
104 | |||
105 | fabric = new_fabric; | ||
106 | |||
107 | attach: | ||
108 | list_for_each_entry(c, &codec_list, list) { | ||
109 | if (c->fabric != fabric) | ||
110 | attach_codec_to_fabric(c); | ||
111 | } | ||
112 | return err; | ||
113 | } | ||
114 | EXPORT_SYMBOL_GPL(aoa_fabric_register); | ||
115 | |||
116 | void aoa_fabric_unregister(struct aoa_fabric *old_fabric) | ||
117 | { | ||
118 | struct aoa_codec *c; | ||
119 | |||
120 | if (fabric != old_fabric) | ||
121 | return; | ||
122 | |||
123 | list_for_each_entry(c, &codec_list, list) { | ||
124 | if (c->fabric) | ||
125 | aoa_fabric_unlink_codec(c); | ||
126 | } | ||
127 | |||
128 | aoa_alsa_cleanup(); | ||
129 | |||
130 | fabric = NULL; | ||
131 | } | ||
132 | EXPORT_SYMBOL_GPL(aoa_fabric_unregister); | ||
133 | |||
134 | void aoa_fabric_unlink_codec(struct aoa_codec *codec) | ||
135 | { | ||
136 | if (!codec->fabric) { | ||
137 | printk(KERN_ERR "snd-aoa: fabric unassigned " | ||
138 | "in aoa_fabric_unlink_codec\n"); | ||
139 | dump_stack(); | ||
140 | return; | ||
141 | } | ||
142 | if (codec->exit) | ||
143 | codec->exit(codec); | ||
144 | if (codec->fabric->remove_codec) | ||
145 | codec->fabric->remove_codec(codec); | ||
146 | codec->fabric = NULL; | ||
147 | module_put(codec->owner); | ||
148 | } | ||
149 | EXPORT_SYMBOL_GPL(aoa_fabric_unlink_codec); | ||
150 | |||
151 | static int __init aoa_init(void) | ||
152 | { | ||
153 | return 0; | ||
154 | } | ||
155 | |||
156 | static void __exit aoa_exit(void) | ||
157 | { | ||
158 | aoa_alsa_cleanup(); | ||
159 | } | ||
160 | |||
161 | module_init(aoa_init); | ||
162 | module_exit(aoa_exit); | ||
diff --git a/sound/aoa/core/snd-aoa-gpio-feature.c b/sound/aoa/core/snd-aoa-gpio-feature.c new file mode 100644 index 000000000000..2c6eb7784cc9 --- /dev/null +++ b/sound/aoa/core/snd-aoa-gpio-feature.c | |||
@@ -0,0 +1,399 @@ | |||
1 | /* | ||
2 | * Apple Onboard Audio feature call GPIO control | ||
3 | * | ||
4 | * Copyright 2006 Johannes Berg <johannes@sipsolutions.net> | ||
5 | * | ||
6 | * GPL v2, can be found in COPYING. | ||
7 | * | ||
8 | * This file contains the GPIO control routines for | ||
9 | * direct (through feature calls) access to the GPIO | ||
10 | * registers. | ||
11 | */ | ||
12 | |||
13 | #include <asm/pmac_feature.h> | ||
14 | #include <linux/interrupt.h> | ||
15 | #include "../aoa.h" | ||
16 | |||
17 | /* TODO: these are 20 global variables | ||
18 | * that aren't used on most machines... | ||
19 | * Move them into a dynamically allocated | ||
20 | * structure and use that. | ||
21 | */ | ||
22 | |||
23 | /* these are the GPIO numbers (register addresses as offsets into | ||
24 | * the GPIO space) */ | ||
25 | static int headphone_mute_gpio; | ||
26 | static int amp_mute_gpio; | ||
27 | static int lineout_mute_gpio; | ||
28 | static int hw_reset_gpio; | ||
29 | static int lineout_detect_gpio; | ||
30 | static int headphone_detect_gpio; | ||
31 | static int linein_detect_gpio; | ||
32 | |||
33 | /* see the SWITCH_GPIO macro */ | ||
34 | static int headphone_mute_gpio_activestate; | ||
35 | static int amp_mute_gpio_activestate; | ||
36 | static int lineout_mute_gpio_activestate; | ||
37 | static int hw_reset_gpio_activestate; | ||
38 | static int lineout_detect_gpio_activestate; | ||
39 | static int headphone_detect_gpio_activestate; | ||
40 | static int linein_detect_gpio_activestate; | ||
41 | |||
42 | /* node pointers that we save when getting the GPIO number | ||
43 | * to get the interrupt later */ | ||
44 | static struct device_node *lineout_detect_node; | ||
45 | static struct device_node *linein_detect_node; | ||
46 | static struct device_node *headphone_detect_node; | ||
47 | |||
48 | static int lineout_detect_irq; | ||
49 | static int linein_detect_irq; | ||
50 | static int headphone_detect_irq; | ||
51 | |||
52 | static struct device_node *get_gpio(char *name, | ||
53 | char *altname, | ||
54 | int *gpioptr, | ||
55 | int *gpioactiveptr) | ||
56 | { | ||
57 | struct device_node *np, *gpio; | ||
58 | u32 *reg; | ||
59 | char *audio_gpio; | ||
60 | |||
61 | *gpioptr = -1; | ||
62 | |||
63 | /* check if we can get it the easy way ... */ | ||
64 | np = of_find_node_by_name(NULL, name); | ||
65 | if (!np) { | ||
66 | /* some machines have only gpioX/extint-gpioX nodes, | ||
67 | * and an audio-gpio property saying what it is ... | ||
68 | * So what we have to do is enumerate all children | ||
69 | * of the gpio node and check them all. */ | ||
70 | gpio = of_find_node_by_name(NULL, "gpio"); | ||
71 | if (!gpio) | ||
72 | return NULL; | ||
73 | while ((np = of_get_next_child(gpio, np))) { | ||
74 | audio_gpio = get_property(np, "audio-gpio", NULL); | ||
75 | if (!audio_gpio) | ||
76 | continue; | ||
77 | if (strcmp(audio_gpio, name) == 0) | ||
78 | break; | ||
79 | if (altname && (strcmp(audio_gpio, altname) == 0)) | ||
80 | break; | ||
81 | } | ||
82 | /* still not found, assume not there */ | ||
83 | if (!np) | ||
84 | return NULL; | ||
85 | } | ||
86 | |||
87 | reg = (u32 *)get_property(np, "reg", NULL); | ||
88 | if (!reg) | ||
89 | return NULL; | ||
90 | |||
91 | *gpioptr = *reg; | ||
92 | |||
93 | /* this is a hack, usually the GPIOs 'reg' property | ||
94 | * should have the offset based from the GPIO space | ||
95 | * which is at 0x50, but apparently not always... */ | ||
96 | if (*gpioptr < 0x50) | ||
97 | *gpioptr += 0x50; | ||
98 | |||
99 | reg = (u32 *)get_property(np, "audio-gpio-active-state", NULL); | ||
100 | if (!reg) | ||
101 | /* Apple seems to default to 1, but | ||
102 | * that doesn't seem right at least on most | ||
103 | * machines. So until proven that the opposite | ||
104 | * is necessary, we default to 0 | ||
105 | * (which, incidentally, snd-powermac also does...) */ | ||
106 | *gpioactiveptr = 0; | ||
107 | else | ||
108 | *gpioactiveptr = *reg; | ||
109 | |||
110 | return np; | ||
111 | } | ||
112 | |||
113 | static void get_irq(struct device_node * np, int *irqptr) | ||
114 | { | ||
115 | *irqptr = -1; | ||
116 | if (!np) | ||
117 | return; | ||
118 | if (np->n_intrs != 1) | ||
119 | return; | ||
120 | *irqptr = np->intrs[0].line; | ||
121 | } | ||
122 | |||
123 | /* 0x4 is outenable, 0x1 is out, thus 4 or 5 */ | ||
124 | #define SWITCH_GPIO(name, v, on) \ | ||
125 | (((v)&~1) | ((on)? \ | ||
126 | (name##_gpio_activestate==0?4:5): \ | ||
127 | (name##_gpio_activestate==0?5:4))) | ||
128 | |||
129 | #define FTR_GPIO(name, bit) \ | ||
130 | static void ftr_gpio_set_##name(struct gpio_runtime *rt, int on)\ | ||
131 | { \ | ||
132 | int v; \ | ||
133 | \ | ||
134 | if (unlikely(!rt)) return; \ | ||
135 | \ | ||
136 | if (name##_mute_gpio < 0) \ | ||
137 | return; \ | ||
138 | \ | ||
139 | v = pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, \ | ||
140 | name##_mute_gpio, \ | ||
141 | 0); \ | ||
142 | \ | ||
143 | /* muted = !on... */ \ | ||
144 | v = SWITCH_GPIO(name##_mute, v, !on); \ | ||
145 | \ | ||
146 | pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, \ | ||
147 | name##_mute_gpio, v); \ | ||
148 | \ | ||
149 | rt->implementation_private &= ~(1<<bit); \ | ||
150 | rt->implementation_private |= (!!on << bit); \ | ||
151 | } \ | ||
152 | static int ftr_gpio_get_##name(struct gpio_runtime *rt) \ | ||
153 | { \ | ||
154 | if (unlikely(!rt)) return 0; \ | ||
155 | return (rt->implementation_private>>bit)&1; \ | ||
156 | } | ||
157 | |||
158 | FTR_GPIO(headphone, 0); | ||
159 | FTR_GPIO(amp, 1); | ||
160 | FTR_GPIO(lineout, 2); | ||
161 | |||
162 | static void ftr_gpio_set_hw_reset(struct gpio_runtime *rt, int on) | ||
163 | { | ||
164 | int v; | ||
165 | |||
166 | if (unlikely(!rt)) return; | ||
167 | if (hw_reset_gpio < 0) | ||
168 | return; | ||
169 | |||
170 | v = pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, | ||
171 | hw_reset_gpio, 0); | ||
172 | v = SWITCH_GPIO(hw_reset, v, on); | ||
173 | pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, | ||
174 | hw_reset_gpio, v); | ||
175 | } | ||
176 | |||
177 | static void ftr_gpio_all_amps_off(struct gpio_runtime *rt) | ||
178 | { | ||
179 | int saved; | ||
180 | |||
181 | if (unlikely(!rt)) return; | ||
182 | saved = rt->implementation_private; | ||
183 | ftr_gpio_set_headphone(rt, 0); | ||
184 | ftr_gpio_set_amp(rt, 0); | ||
185 | ftr_gpio_set_lineout(rt, 0); | ||
186 | rt->implementation_private = saved; | ||
187 | } | ||
188 | |||
189 | static void ftr_gpio_all_amps_restore(struct gpio_runtime *rt) | ||
190 | { | ||
191 | int s; | ||
192 | |||
193 | if (unlikely(!rt)) return; | ||
194 | s = rt->implementation_private; | ||
195 | ftr_gpio_set_headphone(rt, (s>>0)&1); | ||
196 | ftr_gpio_set_amp(rt, (s>>1)&1); | ||
197 | ftr_gpio_set_lineout(rt, (s>>2)&1); | ||
198 | } | ||
199 | |||
200 | static void ftr_handle_notify(void *data) | ||
201 | { | ||
202 | struct gpio_notification *notif = data; | ||
203 | |||
204 | mutex_lock(¬if->mutex); | ||
205 | if (notif->notify) | ||
206 | notif->notify(notif->data); | ||
207 | mutex_unlock(¬if->mutex); | ||
208 | } | ||
209 | |||
210 | static void ftr_gpio_init(struct gpio_runtime *rt) | ||
211 | { | ||
212 | get_gpio("headphone-mute", NULL, | ||
213 | &headphone_mute_gpio, | ||
214 | &headphone_mute_gpio_activestate); | ||
215 | get_gpio("amp-mute", NULL, | ||
216 | &_mute_gpio, | ||
217 | &_mute_gpio_activestate); | ||
218 | get_gpio("lineout-mute", NULL, | ||
219 | &lineout_mute_gpio, | ||
220 | &lineout_mute_gpio_activestate); | ||
221 | get_gpio("hw-reset", "audio-hw-reset", | ||
222 | &hw_reset_gpio, | ||
223 | &hw_reset_gpio_activestate); | ||
224 | |||
225 | headphone_detect_node = get_gpio("headphone-detect", NULL, | ||
226 | &headphone_detect_gpio, | ||
227 | &headphone_detect_gpio_activestate); | ||
228 | /* go Apple, and thanks for giving these different names | ||
229 | * across the board... */ | ||
230 | lineout_detect_node = get_gpio("lineout-detect", "line-output-detect", | ||
231 | &lineout_detect_gpio, | ||
232 | &lineout_detect_gpio_activestate); | ||
233 | linein_detect_node = get_gpio("linein-detect", "line-input-detect", | ||
234 | &linein_detect_gpio, | ||
235 | &linein_detect_gpio_activestate); | ||
236 | |||
237 | get_irq(headphone_detect_node, &headphone_detect_irq); | ||
238 | get_irq(lineout_detect_node, &lineout_detect_irq); | ||
239 | get_irq(linein_detect_node, &linein_detect_irq); | ||
240 | |||
241 | ftr_gpio_all_amps_off(rt); | ||
242 | rt->implementation_private = 0; | ||
243 | INIT_WORK(&rt->headphone_notify.work, ftr_handle_notify, | ||
244 | &rt->headphone_notify); | ||
245 | INIT_WORK(&rt->line_in_notify.work, ftr_handle_notify, | ||
246 | &rt->line_in_notify); | ||
247 | INIT_WORK(&rt->line_out_notify.work, ftr_handle_notify, | ||
248 | &rt->line_out_notify); | ||
249 | mutex_init(&rt->headphone_notify.mutex); | ||
250 | mutex_init(&rt->line_in_notify.mutex); | ||
251 | mutex_init(&rt->line_out_notify.mutex); | ||
252 | } | ||
253 | |||
254 | static void ftr_gpio_exit(struct gpio_runtime *rt) | ||
255 | { | ||
256 | ftr_gpio_all_amps_off(rt); | ||
257 | rt->implementation_private = 0; | ||
258 | if (rt->headphone_notify.notify) | ||
259 | free_irq(headphone_detect_irq, &rt->headphone_notify); | ||
260 | if (rt->line_in_notify.gpio_private) | ||
261 | free_irq(linein_detect_irq, &rt->line_in_notify); | ||
262 | if (rt->line_out_notify.gpio_private) | ||
263 | free_irq(lineout_detect_irq, &rt->line_out_notify); | ||
264 | cancel_delayed_work(&rt->headphone_notify.work); | ||
265 | cancel_delayed_work(&rt->line_in_notify.work); | ||
266 | cancel_delayed_work(&rt->line_out_notify.work); | ||
267 | flush_scheduled_work(); | ||
268 | mutex_destroy(&rt->headphone_notify.mutex); | ||
269 | mutex_destroy(&rt->line_in_notify.mutex); | ||
270 | mutex_destroy(&rt->line_out_notify.mutex); | ||
271 | } | ||
272 | |||
273 | static irqreturn_t ftr_handle_notify_irq(int xx, | ||
274 | void *data, | ||
275 | struct pt_regs *regs) | ||
276 | { | ||
277 | struct gpio_notification *notif = data; | ||
278 | |||
279 | schedule_work(¬if->work); | ||
280 | |||
281 | return IRQ_HANDLED; | ||
282 | } | ||
283 | |||
284 | static int ftr_set_notify(struct gpio_runtime *rt, | ||
285 | enum notify_type type, | ||
286 | notify_func_t notify, | ||
287 | void *data) | ||
288 | { | ||
289 | struct gpio_notification *notif; | ||
290 | notify_func_t old; | ||
291 | int irq; | ||
292 | char *name; | ||
293 | int err = -EBUSY; | ||
294 | |||
295 | switch (type) { | ||
296 | case AOA_NOTIFY_HEADPHONE: | ||
297 | notif = &rt->headphone_notify; | ||
298 | name = "headphone-detect"; | ||
299 | irq = headphone_detect_irq; | ||
300 | break; | ||
301 | case AOA_NOTIFY_LINE_IN: | ||
302 | notif = &rt->line_in_notify; | ||
303 | name = "linein-detect"; | ||
304 | irq = linein_detect_irq; | ||
305 | break; | ||
306 | case AOA_NOTIFY_LINE_OUT: | ||
307 | notif = &rt->line_out_notify; | ||
308 | name = "lineout-detect"; | ||
309 | irq = lineout_detect_irq; | ||
310 | break; | ||
311 | default: | ||
312 | return -EINVAL; | ||
313 | } | ||
314 | |||
315 | if (irq == -1) | ||
316 | return -ENODEV; | ||
317 | |||
318 | mutex_lock(¬if->mutex); | ||
319 | |||
320 | old = notif->notify; | ||
321 | |||
322 | if (!old && !notify) { | ||
323 | err = 0; | ||
324 | goto out_unlock; | ||
325 | } | ||
326 | |||
327 | if (old && notify) { | ||
328 | if (old == notify && notif->data == data) | ||
329 | err = 0; | ||
330 | goto out_unlock; | ||
331 | } | ||
332 | |||
333 | if (old && !notify) | ||
334 | free_irq(irq, notif); | ||
335 | |||
336 | if (!old && notify) { | ||
337 | err = request_irq(irq, ftr_handle_notify_irq, 0, name, notif); | ||
338 | if (err) | ||
339 | goto out_unlock; | ||
340 | } | ||
341 | |||
342 | notif->notify = notify; | ||
343 | notif->data = data; | ||
344 | |||
345 | err = 0; | ||
346 | out_unlock: | ||
347 | mutex_unlock(¬if->mutex); | ||
348 | return err; | ||
349 | } | ||
350 | |||
351 | static int ftr_get_detect(struct gpio_runtime *rt, | ||
352 | enum notify_type type) | ||
353 | { | ||
354 | int gpio, ret, active; | ||
355 | |||
356 | switch (type) { | ||
357 | case AOA_NOTIFY_HEADPHONE: | ||
358 | gpio = headphone_detect_gpio; | ||
359 | active = headphone_detect_gpio_activestate; | ||
360 | break; | ||
361 | case AOA_NOTIFY_LINE_IN: | ||
362 | gpio = linein_detect_gpio; | ||
363 | active = linein_detect_gpio_activestate; | ||
364 | break; | ||
365 | case AOA_NOTIFY_LINE_OUT: | ||
366 | gpio = lineout_detect_gpio; | ||
367 | active = lineout_detect_gpio_activestate; | ||
368 | break; | ||
369 | default: | ||
370 | return -EINVAL; | ||
371 | } | ||
372 | |||
373 | if (gpio == -1) | ||
374 | return -ENODEV; | ||
375 | |||
376 | ret = pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, gpio, 0); | ||
377 | if (ret < 0) | ||
378 | return ret; | ||
379 | return ((ret >> 1) & 1) == active; | ||
380 | } | ||
381 | |||
382 | static struct gpio_methods methods = { | ||
383 | .init = ftr_gpio_init, | ||
384 | .exit = ftr_gpio_exit, | ||
385 | .all_amps_off = ftr_gpio_all_amps_off, | ||
386 | .all_amps_restore = ftr_gpio_all_amps_restore, | ||
387 | .set_headphone = ftr_gpio_set_headphone, | ||
388 | .set_speakers = ftr_gpio_set_amp, | ||
389 | .set_lineout = ftr_gpio_set_lineout, | ||
390 | .set_hw_reset = ftr_gpio_set_hw_reset, | ||
391 | .get_headphone = ftr_gpio_get_headphone, | ||
392 | .get_speakers = ftr_gpio_get_amp, | ||
393 | .get_lineout = ftr_gpio_get_lineout, | ||
394 | .set_notify = ftr_set_notify, | ||
395 | .get_detect = ftr_get_detect, | ||
396 | }; | ||
397 | |||
398 | struct gpio_methods *ftr_gpio_methods = &methods; | ||
399 | EXPORT_SYMBOL_GPL(ftr_gpio_methods); | ||
diff --git a/sound/aoa/core/snd-aoa-gpio-pmf.c b/sound/aoa/core/snd-aoa-gpio-pmf.c new file mode 100644 index 000000000000..0e9b9bb2a6de --- /dev/null +++ b/sound/aoa/core/snd-aoa-gpio-pmf.c | |||
@@ -0,0 +1,246 @@ | |||
1 | /* | ||
2 | * Apple Onboard Audio pmf GPIOs | ||
3 | * | ||
4 | * Copyright 2006 Johannes Berg <johannes@sipsolutions.net> | ||
5 | * | ||
6 | * GPL v2, can be found in COPYING. | ||
7 | */ | ||
8 | |||
9 | #include <asm/pmac_feature.h> | ||
10 | #include <asm/pmac_pfunc.h> | ||
11 | #include "../aoa.h" | ||
12 | |||
13 | #define PMF_GPIO(name, bit) \ | ||
14 | static void pmf_gpio_set_##name(struct gpio_runtime *rt, int on)\ | ||
15 | { \ | ||
16 | struct pmf_args args = { .count = 1, .u[0].v = !on }; \ | ||
17 | \ | ||
18 | if (unlikely(!rt)) return; \ | ||
19 | pmf_call_function(rt->node, #name "-mute", &args); \ | ||
20 | rt->implementation_private &= ~(1<<bit); \ | ||
21 | rt->implementation_private |= (!!on << bit); \ | ||
22 | } \ | ||
23 | static int pmf_gpio_get_##name(struct gpio_runtime *rt) \ | ||
24 | { \ | ||
25 | if (unlikely(!rt)) return 0; \ | ||
26 | return (rt->implementation_private>>bit)&1; \ | ||
27 | } | ||
28 | |||
29 | PMF_GPIO(headphone, 0); | ||
30 | PMF_GPIO(amp, 1); | ||
31 | PMF_GPIO(lineout, 2); | ||
32 | |||
33 | static void pmf_gpio_set_hw_reset(struct gpio_runtime *rt, int on) | ||
34 | { | ||
35 | struct pmf_args args = { .count = 1, .u[0].v = !!on }; | ||
36 | |||
37 | if (unlikely(!rt)) return; | ||
38 | pmf_call_function(rt->node, "hw-reset", &args); | ||
39 | } | ||
40 | |||
41 | static void pmf_gpio_all_amps_off(struct gpio_runtime *rt) | ||
42 | { | ||
43 | int saved; | ||
44 | |||
45 | if (unlikely(!rt)) return; | ||
46 | saved = rt->implementation_private; | ||
47 | pmf_gpio_set_headphone(rt, 0); | ||
48 | pmf_gpio_set_amp(rt, 0); | ||
49 | pmf_gpio_set_lineout(rt, 0); | ||
50 | rt->implementation_private = saved; | ||
51 | } | ||
52 | |||
53 | static void pmf_gpio_all_amps_restore(struct gpio_runtime *rt) | ||
54 | { | ||
55 | int s; | ||
56 | |||
57 | if (unlikely(!rt)) return; | ||
58 | s = rt->implementation_private; | ||
59 | pmf_gpio_set_headphone(rt, (s>>0)&1); | ||
60 | pmf_gpio_set_amp(rt, (s>>1)&1); | ||
61 | pmf_gpio_set_lineout(rt, (s>>2)&1); | ||
62 | } | ||
63 | |||
64 | static void pmf_handle_notify(void *data) | ||
65 | { | ||
66 | struct gpio_notification *notif = data; | ||
67 | |||
68 | mutex_lock(¬if->mutex); | ||
69 | if (notif->notify) | ||
70 | notif->notify(notif->data); | ||
71 | mutex_unlock(¬if->mutex); | ||
72 | } | ||
73 | |||
74 | static void pmf_gpio_init(struct gpio_runtime *rt) | ||
75 | { | ||
76 | pmf_gpio_all_amps_off(rt); | ||
77 | rt->implementation_private = 0; | ||
78 | INIT_WORK(&rt->headphone_notify.work, pmf_handle_notify, | ||
79 | &rt->headphone_notify); | ||
80 | INIT_WORK(&rt->line_in_notify.work, pmf_handle_notify, | ||
81 | &rt->line_in_notify); | ||
82 | INIT_WORK(&rt->line_out_notify.work, pmf_handle_notify, | ||
83 | &rt->line_out_notify); | ||
84 | mutex_init(&rt->headphone_notify.mutex); | ||
85 | mutex_init(&rt->line_in_notify.mutex); | ||
86 | mutex_init(&rt->line_out_notify.mutex); | ||
87 | } | ||
88 | |||
89 | static void pmf_gpio_exit(struct gpio_runtime *rt) | ||
90 | { | ||
91 | pmf_gpio_all_amps_off(rt); | ||
92 | rt->implementation_private = 0; | ||
93 | |||
94 | if (rt->headphone_notify.gpio_private) | ||
95 | pmf_unregister_irq_client(rt->headphone_notify.gpio_private); | ||
96 | if (rt->line_in_notify.gpio_private) | ||
97 | pmf_unregister_irq_client(rt->line_in_notify.gpio_private); | ||
98 | if (rt->line_out_notify.gpio_private) | ||
99 | pmf_unregister_irq_client(rt->line_out_notify.gpio_private); | ||
100 | |||
101 | /* make sure no work is pending before freeing | ||
102 | * all things */ | ||
103 | cancel_delayed_work(&rt->headphone_notify.work); | ||
104 | cancel_delayed_work(&rt->line_in_notify.work); | ||
105 | cancel_delayed_work(&rt->line_out_notify.work); | ||
106 | flush_scheduled_work(); | ||
107 | |||
108 | mutex_destroy(&rt->headphone_notify.mutex); | ||
109 | mutex_destroy(&rt->line_in_notify.mutex); | ||
110 | mutex_destroy(&rt->line_out_notify.mutex); | ||
111 | |||
112 | if (rt->headphone_notify.gpio_private) | ||
113 | kfree(rt->headphone_notify.gpio_private); | ||
114 | if (rt->line_in_notify.gpio_private) | ||
115 | kfree(rt->line_in_notify.gpio_private); | ||
116 | if (rt->line_out_notify.gpio_private) | ||
117 | kfree(rt->line_out_notify.gpio_private); | ||
118 | } | ||
119 | |||
120 | static void pmf_handle_notify_irq(void *data) | ||
121 | { | ||
122 | struct gpio_notification *notif = data; | ||
123 | |||
124 | schedule_work(¬if->work); | ||
125 | } | ||
126 | |||
127 | static int pmf_set_notify(struct gpio_runtime *rt, | ||
128 | enum notify_type type, | ||
129 | notify_func_t notify, | ||
130 | void *data) | ||
131 | { | ||
132 | struct gpio_notification *notif; | ||
133 | notify_func_t old; | ||
134 | struct pmf_irq_client *irq_client; | ||
135 | char *name; | ||
136 | int err = -EBUSY; | ||
137 | |||
138 | switch (type) { | ||
139 | case AOA_NOTIFY_HEADPHONE: | ||
140 | notif = &rt->headphone_notify; | ||
141 | name = "headphone-detect"; | ||
142 | break; | ||
143 | case AOA_NOTIFY_LINE_IN: | ||
144 | notif = &rt->line_in_notify; | ||
145 | name = "linein-detect"; | ||
146 | break; | ||
147 | case AOA_NOTIFY_LINE_OUT: | ||
148 | notif = &rt->line_out_notify; | ||
149 | name = "lineout-detect"; | ||
150 | break; | ||
151 | default: | ||
152 | return -EINVAL; | ||
153 | } | ||
154 | |||
155 | mutex_lock(¬if->mutex); | ||
156 | |||
157 | old = notif->notify; | ||
158 | |||
159 | if (!old && !notify) { | ||
160 | err = 0; | ||
161 | goto out_unlock; | ||
162 | } | ||
163 | |||
164 | if (old && notify) { | ||
165 | if (old == notify && notif->data == data) | ||
166 | err = 0; | ||
167 | goto out_unlock; | ||
168 | } | ||
169 | |||
170 | if (old && !notify) { | ||
171 | irq_client = notif->gpio_private; | ||
172 | pmf_unregister_irq_client(irq_client); | ||
173 | kfree(irq_client); | ||
174 | notif->gpio_private = NULL; | ||
175 | } | ||
176 | if (!old && notify) { | ||
177 | irq_client = kzalloc(sizeof(struct pmf_irq_client), | ||
178 | GFP_KERNEL); | ||
179 | irq_client->data = notif; | ||
180 | irq_client->handler = pmf_handle_notify_irq; | ||
181 | irq_client->owner = THIS_MODULE; | ||
182 | err = pmf_register_irq_client(rt->node, | ||
183 | name, | ||
184 | irq_client); | ||
185 | if (err) { | ||
186 | printk(KERN_ERR "snd-aoa: gpio layer failed to" | ||
187 | " register %s irq (%d)\n", name, err); | ||
188 | kfree(irq_client); | ||
189 | goto out_unlock; | ||
190 | } | ||
191 | notif->gpio_private = irq_client; | ||
192 | } | ||
193 | notif->notify = notify; | ||
194 | notif->data = data; | ||
195 | |||
196 | err = 0; | ||
197 | out_unlock: | ||
198 | mutex_unlock(¬if->mutex); | ||
199 | return err; | ||
200 | } | ||
201 | |||
202 | static int pmf_get_detect(struct gpio_runtime *rt, | ||
203 | enum notify_type type) | ||
204 | { | ||
205 | char *name; | ||
206 | int err = -EBUSY, ret; | ||
207 | struct pmf_args args = { .count = 1, .u[0].p = &ret }; | ||
208 | |||
209 | switch (type) { | ||
210 | case AOA_NOTIFY_HEADPHONE: | ||
211 | name = "headphone-detect"; | ||
212 | break; | ||
213 | case AOA_NOTIFY_LINE_IN: | ||
214 | name = "linein-detect"; | ||
215 | break; | ||
216 | case AOA_NOTIFY_LINE_OUT: | ||
217 | name = "lineout-detect"; | ||
218 | break; | ||
219 | default: | ||
220 | return -EINVAL; | ||
221 | } | ||
222 | |||
223 | err = pmf_call_function(rt->node, name, &args); | ||
224 | if (err) | ||
225 | return err; | ||
226 | return ret; | ||
227 | } | ||
228 | |||
229 | static struct gpio_methods methods = { | ||
230 | .init = pmf_gpio_init, | ||
231 | .exit = pmf_gpio_exit, | ||
232 | .all_amps_off = pmf_gpio_all_amps_off, | ||
233 | .all_amps_restore = pmf_gpio_all_amps_restore, | ||
234 | .set_headphone = pmf_gpio_set_headphone, | ||
235 | .set_speakers = pmf_gpio_set_amp, | ||
236 | .set_lineout = pmf_gpio_set_lineout, | ||
237 | .set_hw_reset = pmf_gpio_set_hw_reset, | ||
238 | .get_headphone = pmf_gpio_get_headphone, | ||
239 | .get_speakers = pmf_gpio_get_amp, | ||
240 | .get_lineout = pmf_gpio_get_lineout, | ||
241 | .set_notify = pmf_set_notify, | ||
242 | .get_detect = pmf_get_detect, | ||
243 | }; | ||
244 | |||
245 | struct gpio_methods *pmf_gpio_methods = &methods; | ||
246 | EXPORT_SYMBOL_GPL(pmf_gpio_methods); | ||
diff --git a/sound/aoa/fabrics/Kconfig b/sound/aoa/fabrics/Kconfig new file mode 100644 index 000000000000..c3bc7705c86a --- /dev/null +++ b/sound/aoa/fabrics/Kconfig | |||
@@ -0,0 +1,12 @@ | |||
1 | config SND_AOA_FABRIC_LAYOUT | ||
2 | tristate "layout-id fabric" | ||
3 | depends SND_AOA | ||
4 | select SND_AOA_SOUNDBUS | ||
5 | select SND_AOA_SOUNDBUS_I2S | ||
6 | ---help--- | ||
7 | This enables the layout-id fabric for the Apple Onboard | ||
8 | Audio driver, the module holding it all together | ||
9 | based on the device-tree's layout-id property. | ||
10 | |||
11 | If you are unsure and have a later Apple machine, | ||
12 | compile it as a module. | ||
diff --git a/sound/aoa/fabrics/Makefile b/sound/aoa/fabrics/Makefile new file mode 100644 index 000000000000..55fc5e7e52cf --- /dev/null +++ b/sound/aoa/fabrics/Makefile | |||
@@ -0,0 +1 @@ | |||
obj-$(CONFIG_SND_AOA_FABRIC_LAYOUT) += snd-aoa-fabric-layout.o | |||
diff --git a/sound/aoa/fabrics/snd-aoa-fabric-layout.c b/sound/aoa/fabrics/snd-aoa-fabric-layout.c new file mode 100644 index 000000000000..04a7238e9494 --- /dev/null +++ b/sound/aoa/fabrics/snd-aoa-fabric-layout.c | |||
@@ -0,0 +1,1109 @@ | |||
1 | /* | ||
2 | * Apple Onboard Audio driver -- layout fabric | ||
3 | * | ||
4 | * Copyright 2006 Johannes Berg <johannes@sipsolutions.net> | ||
5 | * | ||
6 | * GPL v2, can be found in COPYING. | ||
7 | * | ||
8 | * | ||
9 | * This fabric module looks for sound codecs | ||
10 | * based on the layout-id property in the device tree. | ||
11 | * | ||
12 | */ | ||
13 | |||
14 | #include <asm/prom.h> | ||
15 | #include <linux/list.h> | ||
16 | #include <linux/module.h> | ||
17 | #include "../aoa.h" | ||
18 | #include "../soundbus/soundbus.h" | ||
19 | |||
20 | MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>"); | ||
21 | MODULE_LICENSE("GPL"); | ||
22 | MODULE_DESCRIPTION("Layout-ID fabric for snd-aoa"); | ||
23 | |||
24 | #define MAX_CODECS_PER_BUS 2 | ||
25 | |||
26 | /* These are the connections the layout fabric | ||
27 | * knows about. It doesn't really care about the | ||
28 | * input ones, but I thought I'd separate them | ||
29 | * to give them proper names. The thing is that | ||
30 | * Apple usually will distinguish the active output | ||
31 | * by GPIOs, while the active input is set directly | ||
32 | * on the codec. Hence we here tell the codec what | ||
33 | * we think is connected. This information is hard- | ||
34 | * coded below ... */ | ||
35 | #define CC_SPEAKERS (1<<0) | ||
36 | #define CC_HEADPHONE (1<<1) | ||
37 | #define CC_LINEOUT (1<<2) | ||
38 | #define CC_DIGITALOUT (1<<3) | ||
39 | #define CC_LINEIN (1<<4) | ||
40 | #define CC_MICROPHONE (1<<5) | ||
41 | #define CC_DIGITALIN (1<<6) | ||
42 | /* pretty bogus but users complain... | ||
43 | * This is a flag saying that the LINEOUT | ||
44 | * should be renamed to HEADPHONE. | ||
45 | * be careful with input detection! */ | ||
46 | #define CC_LINEOUT_LABELLED_HEADPHONE (1<<7) | ||
47 | |||
48 | struct codec_connection { | ||
49 | /* CC_ flags from above */ | ||
50 | int connected; | ||
51 | /* codec dependent bit to be set in the aoa_codec.connected field. | ||
52 | * This intentionally doesn't have any generic flags because the | ||
53 | * fabric has to know the codec anyway and all codecs might have | ||
54 | * different connectors */ | ||
55 | int codec_bit; | ||
56 | }; | ||
57 | |||
58 | struct codec_connect_info { | ||
59 | char *name; | ||
60 | struct codec_connection *connections; | ||
61 | }; | ||
62 | |||
63 | #define LAYOUT_FLAG_COMBO_LINEOUT_SPDIF (1<<0) | ||
64 | |||
65 | struct layout { | ||
66 | unsigned int layout_id; | ||
67 | struct codec_connect_info codecs[MAX_CODECS_PER_BUS]; | ||
68 | int flags; | ||
69 | |||
70 | /* if busname is not assigned, we use 'Master' below, | ||
71 | * so that our layout table doesn't need to be filled | ||
72 | * too much. | ||
73 | * We only assign these two if we expect to find more | ||
74 | * than one soundbus, i.e. on those machines with | ||
75 | * multiple layout-ids */ | ||
76 | char *busname; | ||
77 | int pcmid; | ||
78 | }; | ||
79 | |||
80 | MODULE_ALIAS("sound-layout-41"); | ||
81 | MODULE_ALIAS("sound-layout-45"); | ||
82 | MODULE_ALIAS("sound-layout-51"); | ||
83 | MODULE_ALIAS("sound-layout-58"); | ||
84 | MODULE_ALIAS("sound-layout-60"); | ||
85 | MODULE_ALIAS("sound-layout-61"); | ||
86 | MODULE_ALIAS("sound-layout-64"); | ||
87 | MODULE_ALIAS("sound-layout-65"); | ||
88 | MODULE_ALIAS("sound-layout-68"); | ||
89 | MODULE_ALIAS("sound-layout-69"); | ||
90 | MODULE_ALIAS("sound-layout-70"); | ||
91 | MODULE_ALIAS("sound-layout-72"); | ||
92 | MODULE_ALIAS("sound-layout-80"); | ||
93 | MODULE_ALIAS("sound-layout-82"); | ||
94 | MODULE_ALIAS("sound-layout-84"); | ||
95 | MODULE_ALIAS("sound-layout-86"); | ||
96 | MODULE_ALIAS("sound-layout-92"); | ||
97 | |||
98 | /* onyx with all but microphone connected */ | ||
99 | static struct codec_connection onyx_connections_nomic[] = { | ||
100 | { | ||
101 | .connected = CC_SPEAKERS | CC_HEADPHONE | CC_LINEOUT, | ||
102 | .codec_bit = 0, | ||
103 | }, | ||
104 | { | ||
105 | .connected = CC_DIGITALOUT, | ||
106 | .codec_bit = 1, | ||
107 | }, | ||
108 | { | ||
109 | .connected = CC_LINEIN, | ||
110 | .codec_bit = 2, | ||
111 | }, | ||
112 | {} /* terminate array by .connected == 0 */ | ||
113 | }; | ||
114 | |||
115 | /* onyx on machines without headphone */ | ||
116 | static struct codec_connection onyx_connections_noheadphones[] = { | ||
117 | { | ||
118 | .connected = CC_SPEAKERS | CC_LINEOUT | | ||
119 | CC_LINEOUT_LABELLED_HEADPHONE, | ||
120 | .codec_bit = 0, | ||
121 | }, | ||
122 | { | ||
123 | .connected = CC_DIGITALOUT, | ||
124 | .codec_bit = 1, | ||
125 | }, | ||
126 | /* FIXME: are these correct? probably not for all the machines | ||
127 | * below ... If not this will need separating. */ | ||
128 | { | ||
129 | .connected = CC_LINEIN, | ||
130 | .codec_bit = 2, | ||
131 | }, | ||
132 | { | ||
133 | .connected = CC_MICROPHONE, | ||
134 | .codec_bit = 3, | ||
135 | }, | ||
136 | {} /* terminate array by .connected == 0 */ | ||
137 | }; | ||
138 | |||
139 | /* onyx on machines with real line-out */ | ||
140 | static struct codec_connection onyx_connections_reallineout[] = { | ||
141 | { | ||
142 | .connected = CC_SPEAKERS | CC_LINEOUT | CC_HEADPHONE, | ||
143 | .codec_bit = 0, | ||
144 | }, | ||
145 | { | ||
146 | .connected = CC_DIGITALOUT, | ||
147 | .codec_bit = 1, | ||
148 | }, | ||
149 | { | ||
150 | .connected = CC_LINEIN, | ||
151 | .codec_bit = 2, | ||
152 | }, | ||
153 | {} /* terminate array by .connected == 0 */ | ||
154 | }; | ||
155 | |||
156 | /* tas on machines without line out */ | ||
157 | static struct codec_connection tas_connections_nolineout[] = { | ||
158 | { | ||
159 | .connected = CC_SPEAKERS | CC_HEADPHONE, | ||
160 | .codec_bit = 0, | ||
161 | }, | ||
162 | { | ||
163 | .connected = CC_LINEIN, | ||
164 | .codec_bit = 2, | ||
165 | }, | ||
166 | { | ||
167 | .connected = CC_MICROPHONE, | ||
168 | .codec_bit = 3, | ||
169 | }, | ||
170 | {} /* terminate array by .connected == 0 */ | ||
171 | }; | ||
172 | |||
173 | /* tas on machines with neither line out nor line in */ | ||
174 | static struct codec_connection tas_connections_noline[] = { | ||
175 | { | ||
176 | .connected = CC_SPEAKERS | CC_HEADPHONE, | ||
177 | .codec_bit = 0, | ||
178 | }, | ||
179 | { | ||
180 | .connected = CC_MICROPHONE, | ||
181 | .codec_bit = 3, | ||
182 | }, | ||
183 | {} /* terminate array by .connected == 0 */ | ||
184 | }; | ||
185 | |||
186 | /* tas on machines without microphone */ | ||
187 | static struct codec_connection tas_connections_nomic[] = { | ||
188 | { | ||
189 | .connected = CC_SPEAKERS | CC_HEADPHONE | CC_LINEOUT, | ||
190 | .codec_bit = 0, | ||
191 | }, | ||
192 | { | ||
193 | .connected = CC_LINEIN, | ||
194 | .codec_bit = 2, | ||
195 | }, | ||
196 | {} /* terminate array by .connected == 0 */ | ||
197 | }; | ||
198 | |||
199 | /* tas on machines with everything connected */ | ||
200 | static struct codec_connection tas_connections_all[] = { | ||
201 | { | ||
202 | .connected = CC_SPEAKERS | CC_HEADPHONE | CC_LINEOUT, | ||
203 | .codec_bit = 0, | ||
204 | }, | ||
205 | { | ||
206 | .connected = CC_LINEIN, | ||
207 | .codec_bit = 2, | ||
208 | }, | ||
209 | { | ||
210 | .connected = CC_MICROPHONE, | ||
211 | .codec_bit = 3, | ||
212 | }, | ||
213 | {} /* terminate array by .connected == 0 */ | ||
214 | }; | ||
215 | |||
216 | static struct codec_connection toonie_connections[] = { | ||
217 | { | ||
218 | .connected = CC_SPEAKERS | CC_HEADPHONE, | ||
219 | .codec_bit = 0, | ||
220 | }, | ||
221 | {} /* terminate array by .connected == 0 */ | ||
222 | }; | ||
223 | |||
224 | static struct codec_connection topaz_input[] = { | ||
225 | { | ||
226 | .connected = CC_DIGITALIN, | ||
227 | .codec_bit = 0, | ||
228 | }, | ||
229 | {} /* terminate array by .connected == 0 */ | ||
230 | }; | ||
231 | |||
232 | static struct codec_connection topaz_output[] = { | ||
233 | { | ||
234 | .connected = CC_DIGITALOUT, | ||
235 | .codec_bit = 1, | ||
236 | }, | ||
237 | {} /* terminate array by .connected == 0 */ | ||
238 | }; | ||
239 | |||
240 | static struct codec_connection topaz_inout[] = { | ||
241 | { | ||
242 | .connected = CC_DIGITALIN, | ||
243 | .codec_bit = 0, | ||
244 | }, | ||
245 | { | ||
246 | .connected = CC_DIGITALOUT, | ||
247 | .codec_bit = 1, | ||
248 | }, | ||
249 | {} /* terminate array by .connected == 0 */ | ||
250 | }; | ||
251 | |||
252 | static struct layout layouts[] = { | ||
253 | /* last PowerBooks (15" Oct 2005) */ | ||
254 | { .layout_id = 82, | ||
255 | .flags = LAYOUT_FLAG_COMBO_LINEOUT_SPDIF, | ||
256 | .codecs[0] = { | ||
257 | .name = "onyx", | ||
258 | .connections = onyx_connections_noheadphones, | ||
259 | }, | ||
260 | .codecs[1] = { | ||
261 | .name = "topaz", | ||
262 | .connections = topaz_input, | ||
263 | }, | ||
264 | }, | ||
265 | /* PowerMac9,1 */ | ||
266 | { .layout_id = 60, | ||
267 | .codecs[0] = { | ||
268 | .name = "onyx", | ||
269 | .connections = onyx_connections_reallineout, | ||
270 | }, | ||
271 | }, | ||
272 | /* PowerMac9,1 */ | ||
273 | { .layout_id = 61, | ||
274 | .codecs[0] = { | ||
275 | .name = "topaz", | ||
276 | .connections = topaz_input, | ||
277 | }, | ||
278 | }, | ||
279 | /* PowerBook5,7 */ | ||
280 | { .layout_id = 64, | ||
281 | .flags = LAYOUT_FLAG_COMBO_LINEOUT_SPDIF, | ||
282 | .codecs[0] = { | ||
283 | .name = "onyx", | ||
284 | .connections = onyx_connections_noheadphones, | ||
285 | }, | ||
286 | }, | ||
287 | /* PowerBook5,7 */ | ||
288 | { .layout_id = 65, | ||
289 | .codecs[0] = { | ||
290 | .name = "topaz", | ||
291 | .connections = topaz_input, | ||
292 | }, | ||
293 | }, | ||
294 | /* PowerBook5,9 [17" Oct 2005] */ | ||
295 | { .layout_id = 84, | ||
296 | .flags = LAYOUT_FLAG_COMBO_LINEOUT_SPDIF, | ||
297 | .codecs[0] = { | ||
298 | .name = "onyx", | ||
299 | .connections = onyx_connections_noheadphones, | ||
300 | }, | ||
301 | .codecs[1] = { | ||
302 | .name = "topaz", | ||
303 | .connections = topaz_input, | ||
304 | }, | ||
305 | }, | ||
306 | /* PowerMac8,1 */ | ||
307 | { .layout_id = 45, | ||
308 | .codecs[0] = { | ||
309 | .name = "onyx", | ||
310 | .connections = onyx_connections_noheadphones, | ||
311 | }, | ||
312 | .codecs[1] = { | ||
313 | .name = "topaz", | ||
314 | .connections = topaz_input, | ||
315 | }, | ||
316 | }, | ||
317 | /* Quad PowerMac (analog in, analog/digital out) */ | ||
318 | { .layout_id = 68, | ||
319 | .codecs[0] = { | ||
320 | .name = "onyx", | ||
321 | .connections = onyx_connections_nomic, | ||
322 | }, | ||
323 | }, | ||
324 | /* Quad PowerMac (digital in) */ | ||
325 | { .layout_id = 69, | ||
326 | .codecs[0] = { | ||
327 | .name = "topaz", | ||
328 | .connections = topaz_input, | ||
329 | }, | ||
330 | .busname = "digital in", .pcmid = 1 }, | ||
331 | /* Early 2005 PowerBook (PowerBook 5,6) */ | ||
332 | { .layout_id = 70, | ||
333 | .codecs[0] = { | ||
334 | .name = "tas", | ||
335 | .connections = tas_connections_nolineout, | ||
336 | }, | ||
337 | }, | ||
338 | /* PowerBook 5,4 */ | ||
339 | { .layout_id = 51, | ||
340 | .codecs[0] = { | ||
341 | .name = "tas", | ||
342 | .connections = tas_connections_nolineout, | ||
343 | }, | ||
344 | }, | ||
345 | /* PowerBook6,7 */ | ||
346 | { .layout_id = 80, | ||
347 | .codecs[0] = { | ||
348 | .name = "tas", | ||
349 | .connections = tas_connections_noline, | ||
350 | }, | ||
351 | }, | ||
352 | /* PowerBook6,8 */ | ||
353 | { .layout_id = 72, | ||
354 | .codecs[0] = { | ||
355 | .name = "tas", | ||
356 | .connections = tas_connections_nolineout, | ||
357 | }, | ||
358 | }, | ||
359 | /* PowerMac8,2 */ | ||
360 | { .layout_id = 86, | ||
361 | .codecs[0] = { | ||
362 | .name = "onyx", | ||
363 | .connections = onyx_connections_nomic, | ||
364 | }, | ||
365 | .codecs[1] = { | ||
366 | .name = "topaz", | ||
367 | .connections = topaz_input, | ||
368 | }, | ||
369 | }, | ||
370 | /* PowerBook6,7 */ | ||
371 | { .layout_id = 92, | ||
372 | .codecs[0] = { | ||
373 | .name = "tas", | ||
374 | .connections = tas_connections_nolineout, | ||
375 | }, | ||
376 | }, | ||
377 | /* PowerMac10,1 (Mac Mini) */ | ||
378 | { .layout_id = 58, | ||
379 | .codecs[0] = { | ||
380 | .name = "toonie", | ||
381 | .connections = toonie_connections, | ||
382 | }, | ||
383 | }, | ||
384 | /* unknown, untested, but this comes from Apple */ | ||
385 | { .layout_id = 41, | ||
386 | .codecs[0] = { | ||
387 | .name = "tas", | ||
388 | .connections = tas_connections_all, | ||
389 | }, | ||
390 | }, | ||
391 | { .layout_id = 36, | ||
392 | .codecs[0] = { | ||
393 | .name = "tas", | ||
394 | .connections = tas_connections_nomic, | ||
395 | }, | ||
396 | .codecs[1] = { | ||
397 | .name = "topaz", | ||
398 | .connections = topaz_inout, | ||
399 | }, | ||
400 | }, | ||
401 | { .layout_id = 47, | ||
402 | .codecs[0] = { | ||
403 | .name = "onyx", | ||
404 | .connections = onyx_connections_noheadphones, | ||
405 | }, | ||
406 | }, | ||
407 | { .layout_id = 48, | ||
408 | .codecs[0] = { | ||
409 | .name = "topaz", | ||
410 | .connections = topaz_input, | ||
411 | }, | ||
412 | }, | ||
413 | { .layout_id = 49, | ||
414 | .codecs[0] = { | ||
415 | .name = "onyx", | ||
416 | .connections = onyx_connections_nomic, | ||
417 | }, | ||
418 | }, | ||
419 | { .layout_id = 50, | ||
420 | .codecs[0] = { | ||
421 | .name = "topaz", | ||
422 | .connections = topaz_input, | ||
423 | }, | ||
424 | }, | ||
425 | { .layout_id = 56, | ||
426 | .codecs[0] = { | ||
427 | .name = "onyx", | ||
428 | .connections = onyx_connections_noheadphones, | ||
429 | }, | ||
430 | }, | ||
431 | { .layout_id = 57, | ||
432 | .codecs[0] = { | ||
433 | .name = "topaz", | ||
434 | .connections = topaz_input, | ||
435 | }, | ||
436 | }, | ||
437 | { .layout_id = 62, | ||
438 | .codecs[0] = { | ||
439 | .name = "onyx", | ||
440 | .connections = onyx_connections_noheadphones, | ||
441 | }, | ||
442 | .codecs[1] = { | ||
443 | .name = "topaz", | ||
444 | .connections = topaz_output, | ||
445 | }, | ||
446 | }, | ||
447 | { .layout_id = 66, | ||
448 | .codecs[0] = { | ||
449 | .name = "onyx", | ||
450 | .connections = onyx_connections_noheadphones, | ||
451 | }, | ||
452 | }, | ||
453 | { .layout_id = 67, | ||
454 | .codecs[0] = { | ||
455 | .name = "topaz", | ||
456 | .connections = topaz_input, | ||
457 | }, | ||
458 | }, | ||
459 | { .layout_id = 76, | ||
460 | .codecs[0] = { | ||
461 | .name = "tas", | ||
462 | .connections = tas_connections_nomic, | ||
463 | }, | ||
464 | .codecs[1] = { | ||
465 | .name = "topaz", | ||
466 | .connections = topaz_inout, | ||
467 | }, | ||
468 | }, | ||
469 | { .layout_id = 90, | ||
470 | .codecs[0] = { | ||
471 | .name = "tas", | ||
472 | .connections = tas_connections_noline, | ||
473 | }, | ||
474 | }, | ||
475 | { .layout_id = 94, | ||
476 | .codecs[0] = { | ||
477 | .name = "onyx", | ||
478 | /* but it has an external mic?? how to select? */ | ||
479 | .connections = onyx_connections_noheadphones, | ||
480 | }, | ||
481 | }, | ||
482 | { .layout_id = 96, | ||
483 | .codecs[0] = { | ||
484 | .name = "onyx", | ||
485 | .connections = onyx_connections_noheadphones, | ||
486 | }, | ||
487 | }, | ||
488 | { .layout_id = 98, | ||
489 | .codecs[0] = { | ||
490 | .name = "toonie", | ||
491 | .connections = toonie_connections, | ||
492 | }, | ||
493 | }, | ||
494 | { .layout_id = 100, | ||
495 | .codecs[0] = { | ||
496 | .name = "topaz", | ||
497 | .connections = topaz_input, | ||
498 | }, | ||
499 | .codecs[1] = { | ||
500 | .name = "onyx", | ||
501 | .connections = onyx_connections_noheadphones, | ||
502 | }, | ||
503 | }, | ||
504 | {} | ||
505 | }; | ||
506 | |||
507 | static struct layout *find_layout_by_id(unsigned int id) | ||
508 | { | ||
509 | struct layout *l; | ||
510 | |||
511 | l = layouts; | ||
512 | while (l->layout_id) { | ||
513 | if (l->layout_id == id) | ||
514 | return l; | ||
515 | l++; | ||
516 | } | ||
517 | return NULL; | ||
518 | } | ||
519 | |||
520 | static void use_layout(struct layout *l) | ||
521 | { | ||
522 | int i; | ||
523 | |||
524 | for (i=0; i<MAX_CODECS_PER_BUS; i++) { | ||
525 | if (l->codecs[i].name) { | ||
526 | request_module("snd-aoa-codec-%s", l->codecs[i].name); | ||
527 | } | ||
528 | } | ||
529 | /* now we wait for the codecs to call us back */ | ||
530 | } | ||
531 | |||
532 | struct layout_dev; | ||
533 | |||
534 | struct layout_dev_ptr { | ||
535 | struct layout_dev *ptr; | ||
536 | }; | ||
537 | |||
538 | struct layout_dev { | ||
539 | struct list_head list; | ||
540 | struct soundbus_dev *sdev; | ||
541 | struct device_node *sound; | ||
542 | struct aoa_codec *codecs[MAX_CODECS_PER_BUS]; | ||
543 | struct layout *layout; | ||
544 | struct gpio_runtime gpio; | ||
545 | |||
546 | /* we need these for headphone/lineout detection */ | ||
547 | struct snd_kcontrol *headphone_ctrl; | ||
548 | struct snd_kcontrol *lineout_ctrl; | ||
549 | struct snd_kcontrol *speaker_ctrl; | ||
550 | struct snd_kcontrol *headphone_detected_ctrl; | ||
551 | struct snd_kcontrol *lineout_detected_ctrl; | ||
552 | |||
553 | struct layout_dev_ptr selfptr_headphone; | ||
554 | struct layout_dev_ptr selfptr_lineout; | ||
555 | |||
556 | u32 have_lineout_detect:1, | ||
557 | have_headphone_detect:1, | ||
558 | switch_on_headphone:1, | ||
559 | switch_on_lineout:1; | ||
560 | }; | ||
561 | |||
562 | static LIST_HEAD(layouts_list); | ||
563 | static int layouts_list_items; | ||
564 | /* this can go away but only if we allow multiple cards, | ||
565 | * make the fabric handle all the card stuff, etc... */ | ||
566 | static struct layout_dev *layout_device; | ||
567 | |||
568 | static int control_info(struct snd_kcontrol *kcontrol, | ||
569 | struct snd_ctl_elem_info *uinfo) | ||
570 | { | ||
571 | uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; | ||
572 | uinfo->count = 1; | ||
573 | uinfo->value.integer.min = 0; | ||
574 | uinfo->value.integer.max = 1; | ||
575 | return 0; | ||
576 | } | ||
577 | |||
578 | #define AMP_CONTROL(n, description) \ | ||
579 | static int n##_control_get(struct snd_kcontrol *kcontrol, \ | ||
580 | struct snd_ctl_elem_value *ucontrol) \ | ||
581 | { \ | ||
582 | struct gpio_runtime *gpio = snd_kcontrol_chip(kcontrol); \ | ||
583 | if (gpio->methods && gpio->methods->get_##n) \ | ||
584 | ucontrol->value.integer.value[0] = \ | ||
585 | gpio->methods->get_##n(gpio); \ | ||
586 | return 0; \ | ||
587 | } \ | ||
588 | static int n##_control_put(struct snd_kcontrol *kcontrol, \ | ||
589 | struct snd_ctl_elem_value *ucontrol) \ | ||
590 | { \ | ||
591 | struct gpio_runtime *gpio = snd_kcontrol_chip(kcontrol); \ | ||
592 | if (gpio->methods && gpio->methods->get_##n) \ | ||
593 | gpio->methods->set_##n(gpio, \ | ||
594 | ucontrol->value.integer.value[0]); \ | ||
595 | return 1; \ | ||
596 | } \ | ||
597 | static struct snd_kcontrol_new n##_ctl = { \ | ||
598 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ | ||
599 | .name = description, \ | ||
600 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \ | ||
601 | .info = control_info, \ | ||
602 | .get = n##_control_get, \ | ||
603 | .put = n##_control_put, \ | ||
604 | } | ||
605 | |||
606 | AMP_CONTROL(headphone, "Headphone Switch"); | ||
607 | AMP_CONTROL(speakers, "Speakers Switch"); | ||
608 | AMP_CONTROL(lineout, "Line-Out Switch"); | ||
609 | |||
610 | static int detect_choice_get(struct snd_kcontrol *kcontrol, | ||
611 | struct snd_ctl_elem_value *ucontrol) | ||
612 | { | ||
613 | struct layout_dev *ldev = snd_kcontrol_chip(kcontrol); | ||
614 | |||
615 | switch (kcontrol->private_value) { | ||
616 | case 0: | ||
617 | ucontrol->value.integer.value[0] = ldev->switch_on_headphone; | ||
618 | break; | ||
619 | case 1: | ||
620 | ucontrol->value.integer.value[0] = ldev->switch_on_lineout; | ||
621 | break; | ||
622 | default: | ||
623 | return -ENODEV; | ||
624 | } | ||
625 | return 0; | ||
626 | } | ||
627 | |||
628 | static int detect_choice_put(struct snd_kcontrol *kcontrol, | ||
629 | struct snd_ctl_elem_value *ucontrol) | ||
630 | { | ||
631 | struct layout_dev *ldev = snd_kcontrol_chip(kcontrol); | ||
632 | |||
633 | switch (kcontrol->private_value) { | ||
634 | case 0: | ||
635 | ldev->switch_on_headphone = !!ucontrol->value.integer.value[0]; | ||
636 | break; | ||
637 | case 1: | ||
638 | ldev->switch_on_lineout = !!ucontrol->value.integer.value[0]; | ||
639 | break; | ||
640 | default: | ||
641 | return -ENODEV; | ||
642 | } | ||
643 | return 1; | ||
644 | } | ||
645 | |||
646 | static struct snd_kcontrol_new headphone_detect_choice = { | ||
647 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
648 | .name = "Headphone Detect Autoswitch", | ||
649 | .info = control_info, | ||
650 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, | ||
651 | .get = detect_choice_get, | ||
652 | .put = detect_choice_put, | ||
653 | .private_value = 0, | ||
654 | }; | ||
655 | |||
656 | static struct snd_kcontrol_new lineout_detect_choice = { | ||
657 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
658 | .name = "Line-Out Detect Autoswitch", | ||
659 | .info = control_info, | ||
660 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, | ||
661 | .get = detect_choice_get, | ||
662 | .put = detect_choice_put, | ||
663 | .private_value = 1, | ||
664 | }; | ||
665 | |||
666 | static int detected_get(struct snd_kcontrol *kcontrol, | ||
667 | struct snd_ctl_elem_value *ucontrol) | ||
668 | { | ||
669 | struct layout_dev *ldev = snd_kcontrol_chip(kcontrol); | ||
670 | int v; | ||
671 | |||
672 | switch (kcontrol->private_value) { | ||
673 | case 0: | ||
674 | v = ldev->gpio.methods->get_detect(&ldev->gpio, | ||
675 | AOA_NOTIFY_HEADPHONE); | ||
676 | break; | ||
677 | case 1: | ||
678 | v = ldev->gpio.methods->get_detect(&ldev->gpio, | ||
679 | AOA_NOTIFY_LINE_OUT); | ||
680 | break; | ||
681 | default: | ||
682 | return -ENODEV; | ||
683 | } | ||
684 | ucontrol->value.integer.value[0] = v; | ||
685 | return 0; | ||
686 | } | ||
687 | |||
688 | static struct snd_kcontrol_new headphone_detected = { | ||
689 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
690 | .name = "Headphone Detected", | ||
691 | .info = control_info, | ||
692 | .access = SNDRV_CTL_ELEM_ACCESS_READ, | ||
693 | .get = detected_get, | ||
694 | .private_value = 0, | ||
695 | }; | ||
696 | |||
697 | static struct snd_kcontrol_new lineout_detected = { | ||
698 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
699 | .name = "Line-Out Detected", | ||
700 | .info = control_info, | ||
701 | .access = SNDRV_CTL_ELEM_ACCESS_READ, | ||
702 | .get = detected_get, | ||
703 | .private_value = 1, | ||
704 | }; | ||
705 | |||
706 | static int check_codec(struct aoa_codec *codec, | ||
707 | struct layout_dev *ldev, | ||
708 | struct codec_connect_info *cci) | ||
709 | { | ||
710 | u32 *ref; | ||
711 | char propname[32]; | ||
712 | struct codec_connection *cc; | ||
713 | |||
714 | /* if the codec has a 'codec' node, we require a reference */ | ||
715 | if (codec->node && (strcmp(codec->node->name, "codec") == 0)) { | ||
716 | snprintf(propname, sizeof(propname), | ||
717 | "platform-%s-codec-ref", codec->name); | ||
718 | ref = (u32*)get_property(ldev->sound, propname, NULL); | ||
719 | if (!ref) { | ||
720 | printk(KERN_INFO "snd-aoa-fabric-layout: " | ||
721 | "required property %s not present\n", propname); | ||
722 | return -ENODEV; | ||
723 | } | ||
724 | if (*ref != codec->node->linux_phandle) { | ||
725 | printk(KERN_INFO "snd-aoa-fabric-layout: " | ||
726 | "%s doesn't match!\n", propname); | ||
727 | return -ENODEV; | ||
728 | } | ||
729 | } else { | ||
730 | if (layouts_list_items != 1) { | ||
731 | printk(KERN_INFO "snd-aoa-fabric-layout: " | ||
732 | "more than one soundbus, but no references.\n"); | ||
733 | return -ENODEV; | ||
734 | } | ||
735 | } | ||
736 | codec->soundbus_dev = ldev->sdev; | ||
737 | codec->gpio = &ldev->gpio; | ||
738 | |||
739 | cc = cci->connections; | ||
740 | if (!cc) | ||
741 | return -EINVAL; | ||
742 | |||
743 | printk(KERN_INFO "snd-aoa-fabric-layout: can use this codec\n"); | ||
744 | |||
745 | codec->connected = 0; | ||
746 | codec->fabric_data = cc; | ||
747 | |||
748 | while (cc->connected) { | ||
749 | codec->connected |= 1<<cc->codec_bit; | ||
750 | cc++; | ||
751 | } | ||
752 | |||
753 | return 0; | ||
754 | } | ||
755 | |||
756 | static int layout_found_codec(struct aoa_codec *codec) | ||
757 | { | ||
758 | struct layout_dev *ldev; | ||
759 | int i; | ||
760 | |||
761 | list_for_each_entry(ldev, &layouts_list, list) { | ||
762 | for (i=0; i<MAX_CODECS_PER_BUS; i++) { | ||
763 | if (!ldev->layout->codecs[i].name) | ||
764 | continue; | ||
765 | if (strcmp(ldev->layout->codecs[i].name, codec->name) == 0) { | ||
766 | if (check_codec(codec, | ||
767 | ldev, | ||
768 | &ldev->layout->codecs[i]) == 0) | ||
769 | return 0; | ||
770 | } | ||
771 | } | ||
772 | } | ||
773 | return -ENODEV; | ||
774 | } | ||
775 | |||
776 | static void layout_remove_codec(struct aoa_codec *codec) | ||
777 | { | ||
778 | int i; | ||
779 | /* here remove the codec from the layout dev's | ||
780 | * codec reference */ | ||
781 | |||
782 | codec->soundbus_dev = NULL; | ||
783 | codec->gpio = NULL; | ||
784 | for (i=0; i<MAX_CODECS_PER_BUS; i++) { | ||
785 | } | ||
786 | } | ||
787 | |||
788 | static void layout_notify(void *data) | ||
789 | { | ||
790 | struct layout_dev_ptr *dptr = data; | ||
791 | struct layout_dev *ldev; | ||
792 | int v, update; | ||
793 | struct snd_kcontrol *detected, *c; | ||
794 | struct snd_card *card = aoa_get_card(); | ||
795 | |||
796 | ldev = dptr->ptr; | ||
797 | if (data == &ldev->selfptr_headphone) { | ||
798 | v = ldev->gpio.methods->get_detect(&ldev->gpio, AOA_NOTIFY_HEADPHONE); | ||
799 | detected = ldev->headphone_detected_ctrl; | ||
800 | update = ldev->switch_on_headphone; | ||
801 | if (update) { | ||
802 | ldev->gpio.methods->set_speakers(&ldev->gpio, !v); | ||
803 | ldev->gpio.methods->set_headphone(&ldev->gpio, v); | ||
804 | ldev->gpio.methods->set_lineout(&ldev->gpio, 0); | ||
805 | } | ||
806 | } else if (data == &ldev->selfptr_lineout) { | ||
807 | v = ldev->gpio.methods->get_detect(&ldev->gpio, AOA_NOTIFY_LINE_OUT); | ||
808 | detected = ldev->lineout_detected_ctrl; | ||
809 | update = ldev->switch_on_lineout; | ||
810 | if (update) { | ||
811 | ldev->gpio.methods->set_speakers(&ldev->gpio, !v); | ||
812 | ldev->gpio.methods->set_headphone(&ldev->gpio, 0); | ||
813 | ldev->gpio.methods->set_lineout(&ldev->gpio, v); | ||
814 | } | ||
815 | } else | ||
816 | return; | ||
817 | |||
818 | if (detected) | ||
819 | snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &detected->id); | ||
820 | if (update) { | ||
821 | c = ldev->headphone_ctrl; | ||
822 | if (c) | ||
823 | snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &c->id); | ||
824 | c = ldev->speaker_ctrl; | ||
825 | if (c) | ||
826 | snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &c->id); | ||
827 | c = ldev->lineout_ctrl; | ||
828 | if (c) | ||
829 | snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &c->id); | ||
830 | } | ||
831 | } | ||
832 | |||
833 | static void layout_attached_codec(struct aoa_codec *codec) | ||
834 | { | ||
835 | struct codec_connection *cc; | ||
836 | struct snd_kcontrol *ctl; | ||
837 | int headphones, lineout; | ||
838 | struct layout_dev *ldev = layout_device; | ||
839 | |||
840 | /* need to add this codec to our codec array! */ | ||
841 | |||
842 | cc = codec->fabric_data; | ||
843 | |||
844 | headphones = codec->gpio->methods->get_detect(codec->gpio, | ||
845 | AOA_NOTIFY_HEADPHONE); | ||
846 | lineout = codec->gpio->methods->get_detect(codec->gpio, | ||
847 | AOA_NOTIFY_LINE_OUT); | ||
848 | |||
849 | while (cc->connected) { | ||
850 | if (cc->connected & CC_SPEAKERS) { | ||
851 | if (headphones <= 0 && lineout <= 0) | ||
852 | ldev->gpio.methods->set_speakers(codec->gpio, 1); | ||
853 | ctl = snd_ctl_new1(&speakers_ctl, codec->gpio); | ||
854 | ldev->speaker_ctrl = ctl; | ||
855 | aoa_snd_ctl_add(ctl); | ||
856 | } | ||
857 | if (cc->connected & CC_HEADPHONE) { | ||
858 | if (headphones == 1) | ||
859 | ldev->gpio.methods->set_headphone(codec->gpio, 1); | ||
860 | ctl = snd_ctl_new1(&headphone_ctl, codec->gpio); | ||
861 | ldev->headphone_ctrl = ctl; | ||
862 | aoa_snd_ctl_add(ctl); | ||
863 | ldev->have_headphone_detect = | ||
864 | !ldev->gpio.methods | ||
865 | ->set_notify(&ldev->gpio, | ||
866 | AOA_NOTIFY_HEADPHONE, | ||
867 | layout_notify, | ||
868 | &ldev->selfptr_headphone); | ||
869 | if (ldev->have_headphone_detect) { | ||
870 | ctl = snd_ctl_new1(&headphone_detect_choice, | ||
871 | ldev); | ||
872 | aoa_snd_ctl_add(ctl); | ||
873 | ctl = snd_ctl_new1(&headphone_detected, | ||
874 | ldev); | ||
875 | ldev->headphone_detected_ctrl = ctl; | ||
876 | aoa_snd_ctl_add(ctl); | ||
877 | } | ||
878 | } | ||
879 | if (cc->connected & CC_LINEOUT) { | ||
880 | if (lineout == 1) | ||
881 | ldev->gpio.methods->set_lineout(codec->gpio, 1); | ||
882 | ctl = snd_ctl_new1(&lineout_ctl, codec->gpio); | ||
883 | if (cc->connected & CC_LINEOUT_LABELLED_HEADPHONE) | ||
884 | strlcpy(ctl->id.name, | ||
885 | "Headphone Switch", sizeof(ctl->id.name)); | ||
886 | ldev->lineout_ctrl = ctl; | ||
887 | aoa_snd_ctl_add(ctl); | ||
888 | ldev->have_lineout_detect = | ||
889 | !ldev->gpio.methods | ||
890 | ->set_notify(&ldev->gpio, | ||
891 | AOA_NOTIFY_LINE_OUT, | ||
892 | layout_notify, | ||
893 | &ldev->selfptr_lineout); | ||
894 | if (ldev->have_lineout_detect) { | ||
895 | ctl = snd_ctl_new1(&lineout_detect_choice, | ||
896 | ldev); | ||
897 | if (cc->connected & CC_LINEOUT_LABELLED_HEADPHONE) | ||
898 | strlcpy(ctl->id.name, | ||
899 | "Headphone Detect Autoswitch", | ||
900 | sizeof(ctl->id.name)); | ||
901 | aoa_snd_ctl_add(ctl); | ||
902 | ctl = snd_ctl_new1(&lineout_detected, | ||
903 | ldev); | ||
904 | if (cc->connected & CC_LINEOUT_LABELLED_HEADPHONE) | ||
905 | strlcpy(ctl->id.name, | ||
906 | "Headphone Detected", | ||
907 | sizeof(ctl->id.name)); | ||
908 | ldev->lineout_detected_ctrl = ctl; | ||
909 | aoa_snd_ctl_add(ctl); | ||
910 | } | ||
911 | } | ||
912 | cc++; | ||
913 | } | ||
914 | /* now update initial state */ | ||
915 | if (ldev->have_headphone_detect) | ||
916 | layout_notify(&ldev->selfptr_headphone); | ||
917 | if (ldev->have_lineout_detect) | ||
918 | layout_notify(&ldev->selfptr_lineout); | ||
919 | } | ||
920 | |||
921 | static struct aoa_fabric layout_fabric = { | ||
922 | .name = "SoundByLayout", | ||
923 | .owner = THIS_MODULE, | ||
924 | .found_codec = layout_found_codec, | ||
925 | .remove_codec = layout_remove_codec, | ||
926 | .attached_codec = layout_attached_codec, | ||
927 | }; | ||
928 | |||
929 | static int aoa_fabric_layout_probe(struct soundbus_dev *sdev) | ||
930 | { | ||
931 | struct device_node *sound = NULL; | ||
932 | unsigned int *layout_id; | ||
933 | struct layout *layout; | ||
934 | struct layout_dev *ldev = NULL; | ||
935 | int err; | ||
936 | |||
937 | /* hm, currently we can only have one ... */ | ||
938 | if (layout_device) | ||
939 | return -ENODEV; | ||
940 | |||
941 | /* by breaking out we keep a reference */ | ||
942 | while ((sound = of_get_next_child(sdev->ofdev.node, sound))) { | ||
943 | if (sound->type && strcasecmp(sound->type, "soundchip") == 0) | ||
944 | break; | ||
945 | } | ||
946 | if (!sound) return -ENODEV; | ||
947 | |||
948 | layout_id = (unsigned int *) get_property(sound, "layout-id", NULL); | ||
949 | if (!layout_id) | ||
950 | goto outnodev; | ||
951 | printk(KERN_INFO "snd-aoa-fabric-layout: found bus with layout %d ", *layout_id); | ||
952 | |||
953 | layout = find_layout_by_id(*layout_id); | ||
954 | if (!layout) { | ||
955 | printk("(no idea how to handle)\n"); | ||
956 | goto outnodev; | ||
957 | } | ||
958 | |||
959 | ldev = kzalloc(sizeof(struct layout_dev), GFP_KERNEL); | ||
960 | if (!ldev) | ||
961 | goto outnodev; | ||
962 | |||
963 | layout_device = ldev; | ||
964 | ldev->sdev = sdev; | ||
965 | ldev->sound = sound; | ||
966 | ldev->layout = layout; | ||
967 | ldev->gpio.node = sound->parent; | ||
968 | switch (layout->layout_id) { | ||
969 | case 41: /* that unknown machine no one seems to have */ | ||
970 | case 51: /* PowerBook5,4 */ | ||
971 | case 58: /* Mac Mini */ | ||
972 | ldev->gpio.methods = ftr_gpio_methods; | ||
973 | break; | ||
974 | default: | ||
975 | ldev->gpio.methods = pmf_gpio_methods; | ||
976 | } | ||
977 | ldev->selfptr_headphone.ptr = ldev; | ||
978 | ldev->selfptr_lineout.ptr = ldev; | ||
979 | sdev->ofdev.dev.driver_data = ldev; | ||
980 | |||
981 | printk("(using)\n"); | ||
982 | list_add(&ldev->list, &layouts_list); | ||
983 | layouts_list_items++; | ||
984 | |||
985 | /* assign these before registering ourselves, so | ||
986 | * callbacks that are done during registration | ||
987 | * already have the values */ | ||
988 | sdev->pcmid = ldev->layout->pcmid; | ||
989 | if (ldev->layout->busname) { | ||
990 | sdev->pcmname = ldev->layout->busname; | ||
991 | } else { | ||
992 | sdev->pcmname = "Master"; | ||
993 | } | ||
994 | |||
995 | ldev->gpio.methods->init(&ldev->gpio); | ||
996 | |||
997 | err = aoa_fabric_register(&layout_fabric); | ||
998 | if (err && err != -EALREADY) { | ||
999 | printk(KERN_INFO "snd-aoa-fabric-layout: can't use," | ||
1000 | " another fabric is active!\n"); | ||
1001 | goto outlistdel; | ||
1002 | } | ||
1003 | |||
1004 | use_layout(layout); | ||
1005 | ldev->switch_on_headphone = 1; | ||
1006 | ldev->switch_on_lineout = 1; | ||
1007 | return 0; | ||
1008 | outlistdel: | ||
1009 | /* we won't be using these then... */ | ||
1010 | ldev->gpio.methods->exit(&ldev->gpio); | ||
1011 | /* reset if we didn't use it */ | ||
1012 | sdev->pcmname = NULL; | ||
1013 | sdev->pcmid = -1; | ||
1014 | list_del(&ldev->list); | ||
1015 | layouts_list_items--; | ||
1016 | outnodev: | ||
1017 | if (sound) of_node_put(sound); | ||
1018 | layout_device = NULL; | ||
1019 | if (ldev) kfree(ldev); | ||
1020 | return -ENODEV; | ||
1021 | } | ||
1022 | |||
1023 | static int aoa_fabric_layout_remove(struct soundbus_dev *sdev) | ||
1024 | { | ||
1025 | struct layout_dev *ldev = sdev->ofdev.dev.driver_data; | ||
1026 | int i; | ||
1027 | |||
1028 | for (i=0; i<MAX_CODECS_PER_BUS; i++) { | ||
1029 | if (ldev->codecs[i]) { | ||
1030 | aoa_fabric_unlink_codec(ldev->codecs[i]); | ||
1031 | } | ||
1032 | ldev->codecs[i] = NULL; | ||
1033 | } | ||
1034 | list_del(&ldev->list); | ||
1035 | layouts_list_items--; | ||
1036 | of_node_put(ldev->sound); | ||
1037 | |||
1038 | ldev->gpio.methods->set_notify(&ldev->gpio, | ||
1039 | AOA_NOTIFY_HEADPHONE, | ||
1040 | NULL, | ||
1041 | NULL); | ||
1042 | ldev->gpio.methods->set_notify(&ldev->gpio, | ||
1043 | AOA_NOTIFY_LINE_OUT, | ||
1044 | NULL, | ||
1045 | NULL); | ||
1046 | |||
1047 | ldev->gpio.methods->exit(&ldev->gpio); | ||
1048 | layout_device = NULL; | ||
1049 | kfree(ldev); | ||
1050 | sdev->pcmid = -1; | ||
1051 | sdev->pcmname = NULL; | ||
1052 | return 0; | ||
1053 | } | ||
1054 | |||
1055 | #ifdef CONFIG_PM | ||
1056 | static int aoa_fabric_layout_suspend(struct soundbus_dev *sdev, pm_message_t state) | ||
1057 | { | ||
1058 | struct layout_dev *ldev = sdev->ofdev.dev.driver_data; | ||
1059 | |||
1060 | printk("aoa_fabric_layout_suspend()\n"); | ||
1061 | |||
1062 | if (ldev->gpio.methods && ldev->gpio.methods->all_amps_off) | ||
1063 | ldev->gpio.methods->all_amps_off(&ldev->gpio); | ||
1064 | |||
1065 | return 0; | ||
1066 | } | ||
1067 | |||
1068 | static int aoa_fabric_layout_resume(struct soundbus_dev *sdev) | ||
1069 | { | ||
1070 | struct layout_dev *ldev = sdev->ofdev.dev.driver_data; | ||
1071 | |||
1072 | printk("aoa_fabric_layout_resume()\n"); | ||
1073 | |||
1074 | if (ldev->gpio.methods && ldev->gpio.methods->all_amps_off) | ||
1075 | ldev->gpio.methods->all_amps_restore(&ldev->gpio); | ||
1076 | |||
1077 | return 0; | ||
1078 | } | ||
1079 | #endif | ||
1080 | |||
1081 | static struct soundbus_driver aoa_soundbus_driver = { | ||
1082 | .name = "snd_aoa_soundbus_drv", | ||
1083 | .owner = THIS_MODULE, | ||
1084 | .probe = aoa_fabric_layout_probe, | ||
1085 | .remove = aoa_fabric_layout_remove, | ||
1086 | #ifdef CONFIG_PM | ||
1087 | .suspend = aoa_fabric_layout_suspend, | ||
1088 | .resume = aoa_fabric_layout_resume, | ||
1089 | #endif | ||
1090 | }; | ||
1091 | |||
1092 | static int __init aoa_fabric_layout_init(void) | ||
1093 | { | ||
1094 | int err; | ||
1095 | |||
1096 | err = soundbus_register_driver(&aoa_soundbus_driver); | ||
1097 | if (err) | ||
1098 | return err; | ||
1099 | return 0; | ||
1100 | } | ||
1101 | |||
1102 | static void __exit aoa_fabric_layout_exit(void) | ||
1103 | { | ||
1104 | soundbus_unregister_driver(&aoa_soundbus_driver); | ||
1105 | aoa_fabric_unregister(&layout_fabric); | ||
1106 | } | ||
1107 | |||
1108 | module_init(aoa_fabric_layout_init); | ||
1109 | module_exit(aoa_fabric_layout_exit); | ||
diff --git a/sound/aoa/soundbus/Kconfig b/sound/aoa/soundbus/Kconfig new file mode 100644 index 000000000000..d532d27a9f54 --- /dev/null +++ b/sound/aoa/soundbus/Kconfig | |||
@@ -0,0 +1,14 @@ | |||
1 | config SND_AOA_SOUNDBUS | ||
2 | tristate "Apple Soundbus support" | ||
3 | depends on SOUND && SND_PCM && EXPERIMENTAL | ||
4 | ---help--- | ||
5 | This option enables the generic driver for the soundbus | ||
6 | support on Apple machines. | ||
7 | |||
8 | It is required for the sound bus implementations. | ||
9 | |||
10 | config SND_AOA_SOUNDBUS_I2S | ||
11 | tristate "I2S bus support" | ||
12 | depends on SND_AOA_SOUNDBUS && PCI | ||
13 | ---help--- | ||
14 | This option enables support for Apple I2S busses. | ||
diff --git a/sound/aoa/soundbus/Makefile b/sound/aoa/soundbus/Makefile new file mode 100644 index 000000000000..0e61f5aa06b5 --- /dev/null +++ b/sound/aoa/soundbus/Makefile | |||
@@ -0,0 +1,3 @@ | |||
1 | obj-$(CONFIG_SND_AOA_SOUNDBUS) += snd-aoa-soundbus.o | ||
2 | snd-aoa-soundbus-objs := core.o sysfs.o | ||
3 | obj-$(CONFIG_SND_AOA_SOUNDBUS_I2S) += i2sbus/ | ||
diff --git a/sound/aoa/soundbus/core.c b/sound/aoa/soundbus/core.c new file mode 100644 index 000000000000..abe84a76c835 --- /dev/null +++ b/sound/aoa/soundbus/core.c | |||
@@ -0,0 +1,250 @@ | |||
1 | /* | ||
2 | * soundbus | ||
3 | * | ||
4 | * Copyright 2006 Johannes Berg <johannes@sipsolutions.net> | ||
5 | * | ||
6 | * GPL v2, can be found in COPYING. | ||
7 | */ | ||
8 | |||
9 | #include <linux/module.h> | ||
10 | #include "soundbus.h" | ||
11 | |||
12 | MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>"); | ||
13 | MODULE_LICENSE("GPL"); | ||
14 | MODULE_DESCRIPTION("Apple Soundbus"); | ||
15 | |||
16 | struct soundbus_dev *soundbus_dev_get(struct soundbus_dev *dev) | ||
17 | { | ||
18 | struct device *tmp; | ||
19 | |||
20 | if (!dev) | ||
21 | return NULL; | ||
22 | tmp = get_device(&dev->ofdev.dev); | ||
23 | if (tmp) | ||
24 | return to_soundbus_device(tmp); | ||
25 | else | ||
26 | return NULL; | ||
27 | } | ||
28 | EXPORT_SYMBOL_GPL(soundbus_dev_get); | ||
29 | |||
30 | void soundbus_dev_put(struct soundbus_dev *dev) | ||
31 | { | ||
32 | if (dev) | ||
33 | put_device(&dev->ofdev.dev); | ||
34 | } | ||
35 | EXPORT_SYMBOL_GPL(soundbus_dev_put); | ||
36 | |||
37 | static int soundbus_probe(struct device *dev) | ||
38 | { | ||
39 | int error = -ENODEV; | ||
40 | struct soundbus_driver *drv; | ||
41 | struct soundbus_dev *soundbus_dev; | ||
42 | |||
43 | drv = to_soundbus_driver(dev->driver); | ||
44 | soundbus_dev = to_soundbus_device(dev); | ||
45 | |||
46 | if (!drv->probe) | ||
47 | return error; | ||
48 | |||
49 | soundbus_dev_get(soundbus_dev); | ||
50 | |||
51 | error = drv->probe(soundbus_dev); | ||
52 | if (error) | ||
53 | soundbus_dev_put(soundbus_dev); | ||
54 | |||
55 | return error; | ||
56 | } | ||
57 | |||
58 | |||
59 | static int soundbus_uevent(struct device *dev, char **envp, int num_envp, | ||
60 | char *buffer, int buffer_size) | ||
61 | { | ||
62 | struct soundbus_dev * soundbus_dev; | ||
63 | struct of_device * of; | ||
64 | char *scratch, *compat, *compat2; | ||
65 | int i = 0; | ||
66 | int length, cplen, cplen2, seen = 0; | ||
67 | |||
68 | if (!dev) | ||
69 | return -ENODEV; | ||
70 | |||
71 | soundbus_dev = to_soundbus_device(dev); | ||
72 | if (!soundbus_dev) | ||
73 | return -ENODEV; | ||
74 | |||
75 | of = &soundbus_dev->ofdev; | ||
76 | |||
77 | /* stuff we want to pass to /sbin/hotplug */ | ||
78 | envp[i++] = scratch = buffer; | ||
79 | length = scnprintf (scratch, buffer_size, "OF_NAME=%s", of->node->name); | ||
80 | ++length; | ||
81 | buffer_size -= length; | ||
82 | if ((buffer_size <= 0) || (i >= num_envp)) | ||
83 | return -ENOMEM; | ||
84 | scratch += length; | ||
85 | |||
86 | envp[i++] = scratch; | ||
87 | length = scnprintf (scratch, buffer_size, "OF_TYPE=%s", of->node->type); | ||
88 | ++length; | ||
89 | buffer_size -= length; | ||
90 | if ((buffer_size <= 0) || (i >= num_envp)) | ||
91 | return -ENOMEM; | ||
92 | scratch += length; | ||
93 | |||
94 | /* Since the compatible field can contain pretty much anything | ||
95 | * it's not really legal to split it out with commas. We split it | ||
96 | * up using a number of environment variables instead. */ | ||
97 | |||
98 | compat = (char *) get_property(of->node, "compatible", &cplen); | ||
99 | compat2 = compat; | ||
100 | cplen2= cplen; | ||
101 | while (compat && cplen > 0) { | ||
102 | envp[i++] = scratch; | ||
103 | length = scnprintf (scratch, buffer_size, | ||
104 | "OF_COMPATIBLE_%d=%s", seen, compat); | ||
105 | ++length; | ||
106 | buffer_size -= length; | ||
107 | if ((buffer_size <= 0) || (i >= num_envp)) | ||
108 | return -ENOMEM; | ||
109 | scratch += length; | ||
110 | length = strlen (compat) + 1; | ||
111 | compat += length; | ||
112 | cplen -= length; | ||
113 | seen++; | ||
114 | } | ||
115 | |||
116 | envp[i++] = scratch; | ||
117 | length = scnprintf (scratch, buffer_size, "OF_COMPATIBLE_N=%d", seen); | ||
118 | ++length; | ||
119 | buffer_size -= length; | ||
120 | if ((buffer_size <= 0) || (i >= num_envp)) | ||
121 | return -ENOMEM; | ||
122 | scratch += length; | ||
123 | |||
124 | envp[i++] = scratch; | ||
125 | length = scnprintf (scratch, buffer_size, "MODALIAS=%s", | ||
126 | soundbus_dev->modalias); | ||
127 | |||
128 | buffer_size -= length; | ||
129 | if ((buffer_size <= 0) || (i >= num_envp)) | ||
130 | return -ENOMEM; | ||
131 | |||
132 | envp[i] = NULL; | ||
133 | |||
134 | return 0; | ||
135 | } | ||
136 | |||
137 | static int soundbus_device_remove(struct device *dev) | ||
138 | { | ||
139 | struct soundbus_dev * soundbus_dev = to_soundbus_device(dev); | ||
140 | struct soundbus_driver * drv = to_soundbus_driver(dev->driver); | ||
141 | |||
142 | if (dev->driver && drv->remove) | ||
143 | drv->remove(soundbus_dev); | ||
144 | soundbus_dev_put(soundbus_dev); | ||
145 | |||
146 | return 0; | ||
147 | } | ||
148 | |||
149 | static void soundbus_device_shutdown(struct device *dev) | ||
150 | { | ||
151 | struct soundbus_dev * soundbus_dev = to_soundbus_device(dev); | ||
152 | struct soundbus_driver * drv = to_soundbus_driver(dev->driver); | ||
153 | |||
154 | if (dev->driver && drv->shutdown) | ||
155 | drv->shutdown(soundbus_dev); | ||
156 | } | ||
157 | |||
158 | #ifdef CONFIG_PM | ||
159 | |||
160 | static int soundbus_device_suspend(struct device *dev, pm_message_t state) | ||
161 | { | ||
162 | struct soundbus_dev * soundbus_dev = to_soundbus_device(dev); | ||
163 | struct soundbus_driver * drv = to_soundbus_driver(dev->driver); | ||
164 | |||
165 | if (dev->driver && drv->suspend) | ||
166 | return drv->suspend(soundbus_dev, state); | ||
167 | return 0; | ||
168 | } | ||
169 | |||
170 | static int soundbus_device_resume(struct device * dev) | ||
171 | { | ||
172 | struct soundbus_dev * soundbus_dev = to_soundbus_device(dev); | ||
173 | struct soundbus_driver * drv = to_soundbus_driver(dev->driver); | ||
174 | |||
175 | if (dev->driver && drv->resume) | ||
176 | return drv->resume(soundbus_dev); | ||
177 | return 0; | ||
178 | } | ||
179 | |||
180 | #endif /* CONFIG_PM */ | ||
181 | |||
182 | extern struct device_attribute soundbus_dev_attrs[]; | ||
183 | |||
184 | static struct bus_type soundbus_bus_type = { | ||
185 | .name = "aoa-soundbus", | ||
186 | .probe = soundbus_probe, | ||
187 | .uevent = soundbus_uevent, | ||
188 | .remove = soundbus_device_remove, | ||
189 | .shutdown = soundbus_device_shutdown, | ||
190 | #ifdef CONFIG_PM | ||
191 | .suspend = soundbus_device_suspend, | ||
192 | .resume = soundbus_device_resume, | ||
193 | #endif | ||
194 | .dev_attrs = soundbus_dev_attrs, | ||
195 | }; | ||
196 | |||
197 | static int __init soundbus_init(void) | ||
198 | { | ||
199 | return bus_register(&soundbus_bus_type); | ||
200 | } | ||
201 | |||
202 | static void __exit soundbus_exit(void) | ||
203 | { | ||
204 | bus_unregister(&soundbus_bus_type); | ||
205 | } | ||
206 | |||
207 | int soundbus_add_one(struct soundbus_dev *dev) | ||
208 | { | ||
209 | static int devcount; | ||
210 | |||
211 | /* sanity checks */ | ||
212 | if (!dev->attach_codec || | ||
213 | !dev->ofdev.node || | ||
214 | dev->pcmname || | ||
215 | dev->pcmid != -1) { | ||
216 | printk(KERN_ERR "soundbus: adding device failed sanity check!\n"); | ||
217 | return -EINVAL; | ||
218 | } | ||
219 | |||
220 | snprintf(dev->ofdev.dev.bus_id, BUS_ID_SIZE, "soundbus:%x", ++devcount); | ||
221 | dev->ofdev.dev.bus = &soundbus_bus_type; | ||
222 | return of_device_register(&dev->ofdev); | ||
223 | } | ||
224 | EXPORT_SYMBOL_GPL(soundbus_add_one); | ||
225 | |||
226 | void soundbus_remove_one(struct soundbus_dev *dev) | ||
227 | { | ||
228 | of_device_unregister(&dev->ofdev); | ||
229 | } | ||
230 | EXPORT_SYMBOL_GPL(soundbus_remove_one); | ||
231 | |||
232 | int soundbus_register_driver(struct soundbus_driver *drv) | ||
233 | { | ||
234 | /* initialize common driver fields */ | ||
235 | drv->driver.name = drv->name; | ||
236 | drv->driver.bus = &soundbus_bus_type; | ||
237 | |||
238 | /* register with core */ | ||
239 | return driver_register(&drv->driver); | ||
240 | } | ||
241 | EXPORT_SYMBOL_GPL(soundbus_register_driver); | ||
242 | |||
243 | void soundbus_unregister_driver(struct soundbus_driver *drv) | ||
244 | { | ||
245 | driver_unregister(&drv->driver); | ||
246 | } | ||
247 | EXPORT_SYMBOL_GPL(soundbus_unregister_driver); | ||
248 | |||
249 | module_init(soundbus_init); | ||
250 | module_exit(soundbus_exit); | ||
diff --git a/sound/aoa/soundbus/i2sbus/Makefile b/sound/aoa/soundbus/i2sbus/Makefile new file mode 100644 index 000000000000..e57a5cf65655 --- /dev/null +++ b/sound/aoa/soundbus/i2sbus/Makefile | |||
@@ -0,0 +1,2 @@ | |||
1 | obj-$(CONFIG_SND_AOA_SOUNDBUS_I2S) += snd-aoa-i2sbus.o | ||
2 | snd-aoa-i2sbus-objs := i2sbus-core.o i2sbus-pcm.o i2sbus-control.o | ||
diff --git a/sound/aoa/soundbus/i2sbus/i2sbus-control.c b/sound/aoa/soundbus/i2sbus/i2sbus-control.c new file mode 100644 index 000000000000..f50407952d3c --- /dev/null +++ b/sound/aoa/soundbus/i2sbus/i2sbus-control.c | |||
@@ -0,0 +1,192 @@ | |||
1 | /* | ||
2 | * i2sbus driver -- bus control routines | ||
3 | * | ||
4 | * Copyright 2006 Johannes Berg <johannes@sipsolutions.net> | ||
5 | * | ||
6 | * GPL v2, can be found in COPYING. | ||
7 | */ | ||
8 | |||
9 | #include <asm/io.h> | ||
10 | #include <linux/delay.h> | ||
11 | #include <asm/prom.h> | ||
12 | #include <asm/macio.h> | ||
13 | #include <asm/pmac_feature.h> | ||
14 | #include <asm/pmac_pfunc.h> | ||
15 | #include "i2sbus.h" | ||
16 | |||
17 | int i2sbus_control_init(struct macio_dev* dev, struct i2sbus_control **c) | ||
18 | { | ||
19 | *c = kzalloc(sizeof(struct i2sbus_control), GFP_KERNEL); | ||
20 | if (!*c) | ||
21 | return -ENOMEM; | ||
22 | |||
23 | INIT_LIST_HEAD(&(*c)->list); | ||
24 | |||
25 | if (of_address_to_resource(dev->ofdev.node, 0, &(*c)->rsrc)) | ||
26 | goto err; | ||
27 | /* we really should be using feature calls instead of mapping | ||
28 | * these registers. It's safe for now since no one else is | ||
29 | * touching them... */ | ||
30 | (*c)->controlregs = ioremap((*c)->rsrc.start, | ||
31 | sizeof(struct i2s_control_regs)); | ||
32 | if (!(*c)->controlregs) | ||
33 | goto err; | ||
34 | |||
35 | return 0; | ||
36 | err: | ||
37 | kfree(*c); | ||
38 | *c = NULL; | ||
39 | return -ENODEV; | ||
40 | } | ||
41 | |||
42 | void i2sbus_control_destroy(struct i2sbus_control *c) | ||
43 | { | ||
44 | iounmap(c->controlregs); | ||
45 | kfree(c); | ||
46 | } | ||
47 | |||
48 | /* this is serialised externally */ | ||
49 | int i2sbus_control_add_dev(struct i2sbus_control *c, | ||
50 | struct i2sbus_dev *i2sdev) | ||
51 | { | ||
52 | struct device_node *np; | ||
53 | |||
54 | np = i2sdev->sound.ofdev.node; | ||
55 | i2sdev->enable = pmf_find_function(np, "enable"); | ||
56 | i2sdev->cell_enable = pmf_find_function(np, "cell-enable"); | ||
57 | i2sdev->clock_enable = pmf_find_function(np, "clock-enable"); | ||
58 | i2sdev->cell_disable = pmf_find_function(np, "cell-disable"); | ||
59 | i2sdev->clock_disable = pmf_find_function(np, "clock-disable"); | ||
60 | |||
61 | /* if the bus number is not 0 or 1 we absolutely need to use | ||
62 | * the platform functions -- there's nothing in Darwin that | ||
63 | * would allow seeing a system behind what the FCRs are then, | ||
64 | * and I don't want to go parsing a bunch of platform functions | ||
65 | * by hand to try finding a system... */ | ||
66 | if (i2sdev->bus_number != 0 && i2sdev->bus_number != 1 && | ||
67 | (!i2sdev->enable || | ||
68 | !i2sdev->cell_enable || !i2sdev->clock_enable || | ||
69 | !i2sdev->cell_disable || !i2sdev->clock_disable)) { | ||
70 | pmf_put_function(i2sdev->enable); | ||
71 | pmf_put_function(i2sdev->cell_enable); | ||
72 | pmf_put_function(i2sdev->clock_enable); | ||
73 | pmf_put_function(i2sdev->cell_disable); | ||
74 | pmf_put_function(i2sdev->clock_disable); | ||
75 | return -ENODEV; | ||
76 | } | ||
77 | |||
78 | list_add(&i2sdev->item, &c->list); | ||
79 | |||
80 | return 0; | ||
81 | } | ||
82 | |||
83 | void i2sbus_control_remove_dev(struct i2sbus_control *c, | ||
84 | struct i2sbus_dev *i2sdev) | ||
85 | { | ||
86 | /* this is serialised externally */ | ||
87 | list_del(&i2sdev->item); | ||
88 | if (list_empty(&c->list)) | ||
89 | i2sbus_control_destroy(c); | ||
90 | } | ||
91 | |||
92 | int i2sbus_control_enable(struct i2sbus_control *c, | ||
93 | struct i2sbus_dev *i2sdev) | ||
94 | { | ||
95 | struct pmf_args args = { .count = 0 }; | ||
96 | int cc; | ||
97 | |||
98 | if (i2sdev->enable) | ||
99 | return pmf_call_one(i2sdev->enable, &args); | ||
100 | |||
101 | switch (i2sdev->bus_number) { | ||
102 | case 0: | ||
103 | cc = in_le32(&c->controlregs->cell_control); | ||
104 | out_le32(&c->controlregs->cell_control, cc | CTRL_CLOCK_INTF_0_ENABLE); | ||
105 | break; | ||
106 | case 1: | ||
107 | cc = in_le32(&c->controlregs->cell_control); | ||
108 | out_le32(&c->controlregs->cell_control, cc | CTRL_CLOCK_INTF_1_ENABLE); | ||
109 | break; | ||
110 | default: | ||
111 | return -ENODEV; | ||
112 | } | ||
113 | return 0; | ||
114 | } | ||
115 | |||
116 | int i2sbus_control_cell(struct i2sbus_control *c, | ||
117 | struct i2sbus_dev *i2sdev, | ||
118 | int enable) | ||
119 | { | ||
120 | struct pmf_args args = { .count = 0 }; | ||
121 | int cc; | ||
122 | |||
123 | switch (enable) { | ||
124 | case 0: | ||
125 | if (i2sdev->cell_disable) | ||
126 | return pmf_call_one(i2sdev->cell_disable, &args); | ||
127 | break; | ||
128 | case 1: | ||
129 | if (i2sdev->cell_enable) | ||
130 | return pmf_call_one(i2sdev->cell_enable, &args); | ||
131 | break; | ||
132 | default: | ||
133 | printk(KERN_ERR "i2sbus: INVALID CELL ENABLE VALUE\n"); | ||
134 | return -ENODEV; | ||
135 | } | ||
136 | switch (i2sdev->bus_number) { | ||
137 | case 0: | ||
138 | cc = in_le32(&c->controlregs->cell_control); | ||
139 | cc &= ~CTRL_CLOCK_CELL_0_ENABLE; | ||
140 | cc |= enable * CTRL_CLOCK_CELL_0_ENABLE; | ||
141 | out_le32(&c->controlregs->cell_control, cc); | ||
142 | break; | ||
143 | case 1: | ||
144 | cc = in_le32(&c->controlregs->cell_control); | ||
145 | cc &= ~CTRL_CLOCK_CELL_1_ENABLE; | ||
146 | cc |= enable * CTRL_CLOCK_CELL_1_ENABLE; | ||
147 | out_le32(&c->controlregs->cell_control, cc); | ||
148 | break; | ||
149 | default: | ||
150 | return -ENODEV; | ||
151 | } | ||
152 | return 0; | ||
153 | } | ||
154 | |||
155 | int i2sbus_control_clock(struct i2sbus_control *c, | ||
156 | struct i2sbus_dev *i2sdev, | ||
157 | int enable) | ||
158 | { | ||
159 | struct pmf_args args = { .count = 0 }; | ||
160 | int cc; | ||
161 | |||
162 | switch (enable) { | ||
163 | case 0: | ||
164 | if (i2sdev->clock_disable) | ||
165 | return pmf_call_one(i2sdev->clock_disable, &args); | ||
166 | break; | ||
167 | case 1: | ||
168 | if (i2sdev->clock_enable) | ||
169 | return pmf_call_one(i2sdev->clock_enable, &args); | ||
170 | break; | ||
171 | default: | ||
172 | printk(KERN_ERR "i2sbus: INVALID CLOCK ENABLE VALUE\n"); | ||
173 | return -ENODEV; | ||
174 | } | ||
175 | switch (i2sdev->bus_number) { | ||
176 | case 0: | ||
177 | cc = in_le32(&c->controlregs->cell_control); | ||
178 | cc &= ~CTRL_CLOCK_CLOCK_0_ENABLE; | ||
179 | cc |= enable * CTRL_CLOCK_CLOCK_0_ENABLE; | ||
180 | out_le32(&c->controlregs->cell_control, cc); | ||
181 | break; | ||
182 | case 1: | ||
183 | cc = in_le32(&c->controlregs->cell_control); | ||
184 | cc &= ~CTRL_CLOCK_CLOCK_1_ENABLE; | ||
185 | cc |= enable * CTRL_CLOCK_CLOCK_1_ENABLE; | ||
186 | out_le32(&c->controlregs->cell_control, cc); | ||
187 | break; | ||
188 | default: | ||
189 | return -ENODEV; | ||
190 | } | ||
191 | return 0; | ||
192 | } | ||
diff --git a/sound/aoa/soundbus/i2sbus/i2sbus-control.h b/sound/aoa/soundbus/i2sbus/i2sbus-control.h new file mode 100644 index 000000000000..bb05550f730b --- /dev/null +++ b/sound/aoa/soundbus/i2sbus/i2sbus-control.h | |||
@@ -0,0 +1,37 @@ | |||
1 | /* | ||
2 | * i2sbus driver -- bus register definitions | ||
3 | * | ||
4 | * Copyright 2006 Johannes Berg <johannes@sipsolutions.net> | ||
5 | * | ||
6 | * GPL v2, can be found in COPYING. | ||
7 | */ | ||
8 | #ifndef __I2SBUS_CONTROLREGS_H | ||
9 | #define __I2SBUS_CONTROLREGS_H | ||
10 | |||
11 | /* i2s control registers, at least what we know about them */ | ||
12 | |||
13 | #define __PAD(m,n) u8 __pad##m[n] | ||
14 | #define _PAD(line, n) __PAD(line, n) | ||
15 | #define PAD(n) _PAD(__LINE__, (n)) | ||
16 | struct i2s_control_regs { | ||
17 | PAD(0x38); | ||
18 | __le32 fcr0; /* 0x38 (unknown) */ | ||
19 | __le32 cell_control; /* 0x3c (fcr1) */ | ||
20 | __le32 fcr2; /* 0x40 (unknown) */ | ||
21 | __le32 fcr3; /* 0x44 (fcr3) */ | ||
22 | __le32 clock_control; /* 0x48 (unknown) */ | ||
23 | PAD(4); | ||
24 | /* total size: 0x50 bytes */ | ||
25 | } __attribute__((__packed__)); | ||
26 | |||
27 | #define CTRL_CLOCK_CELL_0_ENABLE (1<<10) | ||
28 | #define CTRL_CLOCK_CLOCK_0_ENABLE (1<<12) | ||
29 | #define CTRL_CLOCK_SWRESET_0 (1<<11) | ||
30 | #define CTRL_CLOCK_INTF_0_ENABLE (1<<13) | ||
31 | |||
32 | #define CTRL_CLOCK_CELL_1_ENABLE (1<<17) | ||
33 | #define CTRL_CLOCK_CLOCK_1_ENABLE (1<<18) | ||
34 | #define CTRL_CLOCK_SWRESET_1 (1<<19) | ||
35 | #define CTRL_CLOCK_INTF_1_ENABLE (1<<20) | ||
36 | |||
37 | #endif /* __I2SBUS_CONTROLREGS_H */ | ||
diff --git a/sound/aoa/soundbus/i2sbus/i2sbus-core.c b/sound/aoa/soundbus/i2sbus/i2sbus-core.c new file mode 100644 index 000000000000..f268dacdaa00 --- /dev/null +++ b/sound/aoa/soundbus/i2sbus/i2sbus-core.c | |||
@@ -0,0 +1,387 @@ | |||
1 | /* | ||
2 | * i2sbus driver | ||
3 | * | ||
4 | * Copyright 2006 Johannes Berg <johannes@sipsolutions.net> | ||
5 | * | ||
6 | * GPL v2, can be found in COPYING. | ||
7 | */ | ||
8 | |||
9 | #include <linux/module.h> | ||
10 | #include <asm/macio.h> | ||
11 | #include <asm/dbdma.h> | ||
12 | #include <linux/pci.h> | ||
13 | #include <linux/interrupt.h> | ||
14 | #include <sound/driver.h> | ||
15 | #include <sound/core.h> | ||
16 | #include <linux/dma-mapping.h> | ||
17 | #include "../soundbus.h" | ||
18 | #include "i2sbus.h" | ||
19 | |||
20 | MODULE_LICENSE("GPL"); | ||
21 | MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>"); | ||
22 | MODULE_DESCRIPTION("Apple Soundbus: I2S support"); | ||
23 | /* for auto-loading, declare that we handle this weird | ||
24 | * string that macio puts into the relevant device */ | ||
25 | MODULE_ALIAS("of:Ni2sTi2sC"); | ||
26 | |||
27 | static struct of_device_id i2sbus_match[] = { | ||
28 | { .name = "i2s" }, | ||
29 | { } | ||
30 | }; | ||
31 | |||
32 | static int alloc_dbdma_descriptor_ring(struct i2sbus_dev *i2sdev, | ||
33 | struct dbdma_command_mem *r, | ||
34 | int numcmds) | ||
35 | { | ||
36 | /* one more for rounding */ | ||
37 | r->size = (numcmds+1) * sizeof(struct dbdma_cmd); | ||
38 | /* We use the PCI APIs for now until the generic one gets fixed | ||
39 | * enough or until we get some macio-specific versions | ||
40 | */ | ||
41 | r->space = dma_alloc_coherent( | ||
42 | &macio_get_pci_dev(i2sdev->macio)->dev, | ||
43 | r->size, | ||
44 | &r->bus_addr, | ||
45 | GFP_KERNEL); | ||
46 | |||
47 | if (!r->space) return -ENOMEM; | ||
48 | |||
49 | memset(r->space, 0, r->size); | ||
50 | r->cmds = (void*)DBDMA_ALIGN(r->space); | ||
51 | r->bus_cmd_start = r->bus_addr + | ||
52 | (dma_addr_t)((char*)r->cmds - (char*)r->space); | ||
53 | |||
54 | return 0; | ||
55 | } | ||
56 | |||
57 | static void free_dbdma_descriptor_ring(struct i2sbus_dev *i2sdev, | ||
58 | struct dbdma_command_mem *r) | ||
59 | { | ||
60 | if (!r->space) return; | ||
61 | |||
62 | dma_free_coherent(&macio_get_pci_dev(i2sdev->macio)->dev, | ||
63 | r->size, r->space, r->bus_addr); | ||
64 | } | ||
65 | |||
66 | static void i2sbus_release_dev(struct device *dev) | ||
67 | { | ||
68 | struct i2sbus_dev *i2sdev; | ||
69 | int i; | ||
70 | |||
71 | i2sdev = container_of(dev, struct i2sbus_dev, sound.ofdev.dev); | ||
72 | |||
73 | if (i2sdev->intfregs) iounmap(i2sdev->intfregs); | ||
74 | if (i2sdev->out.dbdma) iounmap(i2sdev->out.dbdma); | ||
75 | if (i2sdev->in.dbdma) iounmap(i2sdev->in.dbdma); | ||
76 | for (i=0;i<3;i++) | ||
77 | if (i2sdev->allocated_resource[i]) | ||
78 | release_and_free_resource(i2sdev->allocated_resource[i]); | ||
79 | free_dbdma_descriptor_ring(i2sdev, &i2sdev->out.dbdma_ring); | ||
80 | free_dbdma_descriptor_ring(i2sdev, &i2sdev->in.dbdma_ring); | ||
81 | for (i=0;i<3;i++) | ||
82 | free_irq(i2sdev->interrupts[i], i2sdev); | ||
83 | i2sbus_control_remove_dev(i2sdev->control, i2sdev); | ||
84 | mutex_destroy(&i2sdev->lock); | ||
85 | kfree(i2sdev); | ||
86 | } | ||
87 | |||
88 | static irqreturn_t i2sbus_bus_intr(int irq, void *devid, struct pt_regs *regs) | ||
89 | { | ||
90 | struct i2sbus_dev *dev = devid; | ||
91 | u32 intreg; | ||
92 | |||
93 | spin_lock(&dev->low_lock); | ||
94 | intreg = in_le32(&dev->intfregs->intr_ctl); | ||
95 | |||
96 | /* acknowledge interrupt reasons */ | ||
97 | out_le32(&dev->intfregs->intr_ctl, intreg); | ||
98 | |||
99 | spin_unlock(&dev->low_lock); | ||
100 | |||
101 | return IRQ_HANDLED; | ||
102 | } | ||
103 | |||
104 | static int force; | ||
105 | module_param(force, int, 0444); | ||
106 | MODULE_PARM_DESC(force, "Force loading i2sbus even when" | ||
107 | " no layout-id property is present"); | ||
108 | |||
109 | /* FIXME: look at device node refcounting */ | ||
110 | static int i2sbus_add_dev(struct macio_dev *macio, | ||
111 | struct i2sbus_control *control, | ||
112 | struct device_node *np) | ||
113 | { | ||
114 | struct i2sbus_dev *dev; | ||
115 | struct device_node *child = NULL, *sound = NULL; | ||
116 | int i; | ||
117 | static const char *rnames[] = { "i2sbus: %s (control)", | ||
118 | "i2sbus: %s (tx)", | ||
119 | "i2sbus: %s (rx)" }; | ||
120 | static irqreturn_t (*ints[])(int irq, void *devid, | ||
121 | struct pt_regs *regs) = { | ||
122 | i2sbus_bus_intr, | ||
123 | i2sbus_tx_intr, | ||
124 | i2sbus_rx_intr | ||
125 | }; | ||
126 | |||
127 | if (strlen(np->name) != 5) | ||
128 | return 0; | ||
129 | if (strncmp(np->name, "i2s-", 4)) | ||
130 | return 0; | ||
131 | |||
132 | if (np->n_intrs != 3) | ||
133 | return 0; | ||
134 | |||
135 | dev = kzalloc(sizeof(struct i2sbus_dev), GFP_KERNEL); | ||
136 | if (!dev) | ||
137 | return 0; | ||
138 | |||
139 | i = 0; | ||
140 | while ((child = of_get_next_child(np, child))) { | ||
141 | if (strcmp(child->name, "sound") == 0) { | ||
142 | i++; | ||
143 | sound = child; | ||
144 | } | ||
145 | } | ||
146 | if (i == 1) { | ||
147 | u32 *layout_id; | ||
148 | layout_id = (u32*) get_property(sound, "layout-id", NULL); | ||
149 | if (layout_id) { | ||
150 | snprintf(dev->sound.modalias, 32, | ||
151 | "sound-layout-%d", *layout_id); | ||
152 | force = 1; | ||
153 | } | ||
154 | } | ||
155 | /* for the time being, until we can handle non-layout-id | ||
156 | * things in some fabric, refuse to attach if there is no | ||
157 | * layout-id property or we haven't been forced to attach. | ||
158 | * When there are two i2s busses and only one has a layout-id, | ||
159 | * then this depends on the order, but that isn't important | ||
160 | * either as the second one in that case is just a modem. */ | ||
161 | if (!force) { | ||
162 | kfree(dev); | ||
163 | return -ENODEV; | ||
164 | } | ||
165 | |||
166 | mutex_init(&dev->lock); | ||
167 | spin_lock_init(&dev->low_lock); | ||
168 | dev->sound.ofdev.node = np; | ||
169 | dev->sound.ofdev.dma_mask = macio->ofdev.dma_mask; | ||
170 | dev->sound.ofdev.dev.dma_mask = &dev->sound.ofdev.dma_mask; | ||
171 | dev->sound.ofdev.dev.parent = &macio->ofdev.dev; | ||
172 | dev->sound.ofdev.dev.release = i2sbus_release_dev; | ||
173 | dev->sound.attach_codec = i2sbus_attach_codec; | ||
174 | dev->sound.detach_codec = i2sbus_detach_codec; | ||
175 | dev->sound.pcmid = -1; | ||
176 | dev->macio = macio; | ||
177 | dev->control = control; | ||
178 | dev->bus_number = np->name[4] - 'a'; | ||
179 | INIT_LIST_HEAD(&dev->sound.codec_list); | ||
180 | |||
181 | for (i=0;i<3;i++) { | ||
182 | dev->interrupts[i] = -1; | ||
183 | snprintf(dev->rnames[i], sizeof(dev->rnames[i]), rnames[i], np->name); | ||
184 | } | ||
185 | for (i=0;i<3;i++) { | ||
186 | if (request_irq(np->intrs[i].line, ints[i], 0, dev->rnames[i], dev)) | ||
187 | goto err; | ||
188 | dev->interrupts[i] = np->intrs[i].line; | ||
189 | } | ||
190 | |||
191 | for (i=0;i<3;i++) { | ||
192 | if (of_address_to_resource(np, i, &dev->resources[i])) | ||
193 | goto err; | ||
194 | /* if only we could use our resource dev->resources[i]... | ||
195 | * but request_resource doesn't know about parents and | ||
196 | * contained resources... */ | ||
197 | dev->allocated_resource[i] = | ||
198 | request_mem_region(dev->resources[i].start, | ||
199 | dev->resources[i].end - | ||
200 | dev->resources[i].start + 1, | ||
201 | dev->rnames[i]); | ||
202 | if (!dev->allocated_resource[i]) { | ||
203 | printk(KERN_ERR "i2sbus: failed to claim resource %d!\n", i); | ||
204 | goto err; | ||
205 | } | ||
206 | } | ||
207 | /* should do sanity checking here about length of them */ | ||
208 | dev->intfregs = ioremap(dev->resources[0].start, | ||
209 | dev->resources[0].end-dev->resources[0].start+1); | ||
210 | dev->out.dbdma = ioremap(dev->resources[1].start, | ||
211 | dev->resources[1].end-dev->resources[1].start+1); | ||
212 | dev->in.dbdma = ioremap(dev->resources[2].start, | ||
213 | dev->resources[2].end-dev->resources[2].start+1); | ||
214 | if (!dev->intfregs || !dev->out.dbdma || !dev->in.dbdma) | ||
215 | goto err; | ||
216 | |||
217 | if (alloc_dbdma_descriptor_ring(dev, &dev->out.dbdma_ring, | ||
218 | MAX_DBDMA_COMMANDS)) | ||
219 | goto err; | ||
220 | if (alloc_dbdma_descriptor_ring(dev, &dev->in.dbdma_ring, | ||
221 | MAX_DBDMA_COMMANDS)) | ||
222 | goto err; | ||
223 | |||
224 | if (i2sbus_control_add_dev(dev->control, dev)) { | ||
225 | printk(KERN_ERR "i2sbus: control layer didn't like bus\n"); | ||
226 | goto err; | ||
227 | } | ||
228 | |||
229 | if (soundbus_add_one(&dev->sound)) { | ||
230 | printk(KERN_DEBUG "i2sbus: device registration error!\n"); | ||
231 | goto err; | ||
232 | } | ||
233 | |||
234 | /* enable this cell */ | ||
235 | i2sbus_control_cell(dev->control, dev, 1); | ||
236 | i2sbus_control_enable(dev->control, dev); | ||
237 | i2sbus_control_clock(dev->control, dev, 1); | ||
238 | |||
239 | return 1; | ||
240 | err: | ||
241 | for (i=0;i<3;i++) | ||
242 | if (dev->interrupts[i] != -1) | ||
243 | free_irq(dev->interrupts[i], dev); | ||
244 | free_dbdma_descriptor_ring(dev, &dev->out.dbdma_ring); | ||
245 | free_dbdma_descriptor_ring(dev, &dev->in.dbdma_ring); | ||
246 | if (dev->intfregs) iounmap(dev->intfregs); | ||
247 | if (dev->out.dbdma) iounmap(dev->out.dbdma); | ||
248 | if (dev->in.dbdma) iounmap(dev->in.dbdma); | ||
249 | for (i=0;i<3;i++) | ||
250 | if (dev->allocated_resource[i]) | ||
251 | release_and_free_resource(dev->allocated_resource[i]); | ||
252 | mutex_destroy(&dev->lock); | ||
253 | kfree(dev); | ||
254 | return 0; | ||
255 | } | ||
256 | |||
257 | static int i2sbus_probe(struct macio_dev* dev, const struct of_device_id *match) | ||
258 | { | ||
259 | struct device_node *np = NULL; | ||
260 | int got = 0, err; | ||
261 | struct i2sbus_control *control = NULL; | ||
262 | |||
263 | err = i2sbus_control_init(dev, &control); | ||
264 | if (err) | ||
265 | return err; | ||
266 | if (!control) { | ||
267 | printk(KERN_ERR "i2sbus_control_init API breakage\n"); | ||
268 | return -ENODEV; | ||
269 | } | ||
270 | |||
271 | while ((np = of_get_next_child(dev->ofdev.node, np))) { | ||
272 | if (device_is_compatible(np, "i2sbus") || | ||
273 | device_is_compatible(np, "i2s-modem")) { | ||
274 | got += i2sbus_add_dev(dev, control, np); | ||
275 | } | ||
276 | } | ||
277 | |||
278 | if (!got) { | ||
279 | /* found none, clean up */ | ||
280 | i2sbus_control_destroy(control); | ||
281 | return -ENODEV; | ||
282 | } | ||
283 | |||
284 | dev->ofdev.dev.driver_data = control; | ||
285 | |||
286 | return 0; | ||
287 | } | ||
288 | |||
289 | static int i2sbus_remove(struct macio_dev* dev) | ||
290 | { | ||
291 | struct i2sbus_control *control = dev->ofdev.dev.driver_data; | ||
292 | struct i2sbus_dev *i2sdev, *tmp; | ||
293 | |||
294 | list_for_each_entry_safe(i2sdev, tmp, &control->list, item) | ||
295 | soundbus_remove_one(&i2sdev->sound); | ||
296 | |||
297 | return 0; | ||
298 | } | ||
299 | |||
300 | #ifdef CONFIG_PM | ||
301 | static int i2sbus_suspend(struct macio_dev* dev, pm_message_t state) | ||
302 | { | ||
303 | struct i2sbus_control *control = dev->ofdev.dev.driver_data; | ||
304 | struct codec_info_item *cii; | ||
305 | struct i2sbus_dev* i2sdev; | ||
306 | int err, ret = 0; | ||
307 | |||
308 | list_for_each_entry(i2sdev, &control->list, item) { | ||
309 | /* Notify Alsa */ | ||
310 | if (i2sdev->sound.pcm) { | ||
311 | /* Suspend PCM streams */ | ||
312 | snd_pcm_suspend_all(i2sdev->sound.pcm); | ||
313 | /* Probably useless as we handle | ||
314 | * power transitions ourselves */ | ||
315 | snd_power_change_state(i2sdev->sound.pcm->card, | ||
316 | SNDRV_CTL_POWER_D3hot); | ||
317 | } | ||
318 | /* Notify codecs */ | ||
319 | list_for_each_entry(cii, &i2sdev->sound.codec_list, list) { | ||
320 | err = 0; | ||
321 | if (cii->codec->suspend) | ||
322 | err = cii->codec->suspend(cii, state); | ||
323 | if (err) | ||
324 | ret = err; | ||
325 | } | ||
326 | } | ||
327 | return ret; | ||
328 | } | ||
329 | |||
330 | static int i2sbus_resume(struct macio_dev* dev) | ||
331 | { | ||
332 | struct i2sbus_control *control = dev->ofdev.dev.driver_data; | ||
333 | struct codec_info_item *cii; | ||
334 | struct i2sbus_dev* i2sdev; | ||
335 | int err, ret = 0; | ||
336 | |||
337 | list_for_each_entry(i2sdev, &control->list, item) { | ||
338 | /* Notify codecs so they can re-initialize */ | ||
339 | list_for_each_entry(cii, &i2sdev->sound.codec_list, list) { | ||
340 | err = 0; | ||
341 | if (cii->codec->resume) | ||
342 | err = cii->codec->resume(cii); | ||
343 | if (err) | ||
344 | ret = err; | ||
345 | } | ||
346 | /* Notify Alsa */ | ||
347 | if (i2sdev->sound.pcm) { | ||
348 | /* Same comment as above, probably useless */ | ||
349 | snd_power_change_state(i2sdev->sound.pcm->card, | ||
350 | SNDRV_CTL_POWER_D0); | ||
351 | } | ||
352 | } | ||
353 | |||
354 | return ret; | ||
355 | } | ||
356 | #endif /* CONFIG_PM */ | ||
357 | |||
358 | static int i2sbus_shutdown(struct macio_dev* dev) | ||
359 | { | ||
360 | return 0; | ||
361 | } | ||
362 | |||
363 | static struct macio_driver i2sbus_drv = { | ||
364 | .name = "soundbus-i2s", | ||
365 | .owner = THIS_MODULE, | ||
366 | .match_table = i2sbus_match, | ||
367 | .probe = i2sbus_probe, | ||
368 | .remove = i2sbus_remove, | ||
369 | #ifdef CONFIG_PM | ||
370 | .suspend = i2sbus_suspend, | ||
371 | .resume = i2sbus_resume, | ||
372 | #endif | ||
373 | .shutdown = i2sbus_shutdown, | ||
374 | }; | ||
375 | |||
376 | static int __init soundbus_i2sbus_init(void) | ||
377 | { | ||
378 | return macio_register_driver(&i2sbus_drv); | ||
379 | } | ||
380 | |||
381 | static void __exit soundbus_i2sbus_exit(void) | ||
382 | { | ||
383 | macio_unregister_driver(&i2sbus_drv); | ||
384 | } | ||
385 | |||
386 | module_init(soundbus_i2sbus_init); | ||
387 | module_exit(soundbus_i2sbus_exit); | ||
diff --git a/sound/aoa/soundbus/i2sbus/i2sbus-interface.h b/sound/aoa/soundbus/i2sbus/i2sbus-interface.h new file mode 100644 index 000000000000..c6b5f5452d20 --- /dev/null +++ b/sound/aoa/soundbus/i2sbus/i2sbus-interface.h | |||
@@ -0,0 +1,187 @@ | |||
1 | /* | ||
2 | * i2sbus driver -- interface register definitions | ||
3 | * | ||
4 | * Copyright 2006 Johannes Berg <johannes@sipsolutions.net> | ||
5 | * | ||
6 | * GPL v2, can be found in COPYING. | ||
7 | */ | ||
8 | #ifndef __I2SBUS_INTERFACE_H | ||
9 | #define __I2SBUS_INTERFACE_H | ||
10 | |||
11 | /* i2s bus control registers, at least what we know about them */ | ||
12 | |||
13 | #define __PAD(m,n) u8 __pad##m[n] | ||
14 | #define _PAD(line, n) __PAD(line, n) | ||
15 | #define PAD(n) _PAD(__LINE__, (n)) | ||
16 | struct i2s_interface_regs { | ||
17 | __le32 intr_ctl; /* 0x00 */ | ||
18 | PAD(12); | ||
19 | __le32 serial_format; /* 0x10 */ | ||
20 | PAD(12); | ||
21 | __le32 codec_msg_out; /* 0x20 */ | ||
22 | PAD(12); | ||
23 | __le32 codec_msg_in; /* 0x30 */ | ||
24 | PAD(12); | ||
25 | __le32 frame_count; /* 0x40 */ | ||
26 | PAD(12); | ||
27 | __le32 frame_match; /* 0x50 */ | ||
28 | PAD(12); | ||
29 | __le32 data_word_sizes; /* 0x60 */ | ||
30 | PAD(12); | ||
31 | __le32 peak_level_sel; /* 0x70 */ | ||
32 | PAD(12); | ||
33 | __le32 peak_level_in0; /* 0x80 */ | ||
34 | PAD(12); | ||
35 | __le32 peak_level_in1; /* 0x90 */ | ||
36 | PAD(12); | ||
37 | /* total size: 0x100 bytes */ | ||
38 | } __attribute__((__packed__)); | ||
39 | |||
40 | /* interrupt register is just a bitfield with | ||
41 | * interrupt enable and pending bits */ | ||
42 | #define I2S_REG_INTR_CTL 0x00 | ||
43 | # define I2S_INT_FRAME_COUNT (1<<31) | ||
44 | # define I2S_PENDING_FRAME_COUNT (1<<30) | ||
45 | # define I2S_INT_MESSAGE_FLAG (1<<29) | ||
46 | # define I2S_PENDING_MESSAGE_FLAG (1<<28) | ||
47 | # define I2S_INT_NEW_PEAK (1<<27) | ||
48 | # define I2S_PENDING_NEW_PEAK (1<<26) | ||
49 | # define I2S_INT_CLOCKS_STOPPED (1<<25) | ||
50 | # define I2S_PENDING_CLOCKS_STOPPED (1<<24) | ||
51 | # define I2S_INT_EXTERNAL_SYNC_ERROR (1<<23) | ||
52 | # define I2S_PENDING_EXTERNAL_SYNC_ERROR (1<<22) | ||
53 | # define I2S_INT_EXTERNAL_SYNC_OK (1<<21) | ||
54 | # define I2S_PENDING_EXTERNAL_SYNC_OK (1<<20) | ||
55 | # define I2S_INT_NEW_SAMPLE_RATE (1<<19) | ||
56 | # define I2S_PENDING_NEW_SAMPLE_RATE (1<<18) | ||
57 | # define I2S_INT_STATUS_FLAG (1<<17) | ||
58 | # define I2S_PENDING_STATUS_FLAG (1<<16) | ||
59 | |||
60 | /* serial format register is more interesting :) | ||
61 | * It contains: | ||
62 | * - clock source | ||
63 | * - MClk divisor | ||
64 | * - SClk divisor | ||
65 | * - SClk master flag | ||
66 | * - serial format (sony, i2s 64x, i2s 32x, dav, silabs) | ||
67 | * - external sample frequency interrupt (don't understand) | ||
68 | * - external sample frequency | ||
69 | */ | ||
70 | #define I2S_REG_SERIAL_FORMAT 0x10 | ||
71 | /* clock source. You get either 18.432, 45.1584 or 49.1520 MHz */ | ||
72 | # define I2S_SF_CLOCK_SOURCE_SHIFT 30 | ||
73 | # define I2S_SF_CLOCK_SOURCE_MASK (3<<I2S_SF_CLOCK_SOURCE_SHIFT) | ||
74 | # define I2S_SF_CLOCK_SOURCE_18MHz (0<<I2S_SF_CLOCK_SOURCE_SHIFT) | ||
75 | # define I2S_SF_CLOCK_SOURCE_45MHz (1<<I2S_SF_CLOCK_SOURCE_SHIFT) | ||
76 | # define I2S_SF_CLOCK_SOURCE_49MHz (2<<I2S_SF_CLOCK_SOURCE_SHIFT) | ||
77 | /* also, let's define the exact clock speeds here, in Hz */ | ||
78 | #define I2S_CLOCK_SPEED_18MHz 18432000 | ||
79 | #define I2S_CLOCK_SPEED_45MHz 45158400 | ||
80 | #define I2S_CLOCK_SPEED_49MHz 49152000 | ||
81 | /* MClk is the clock that drives the codec, usually called its 'system clock'. | ||
82 | * It is derived by taking only every 'divisor' tick of the clock. | ||
83 | */ | ||
84 | # define I2S_SF_MCLKDIV_SHIFT 24 | ||
85 | # define I2S_SF_MCLKDIV_MASK (0x1F<<I2S_SF_MCLKDIV_SHIFT) | ||
86 | # define I2S_SF_MCLKDIV_1 (0x14<<I2S_SF_MCLKDIV_SHIFT) | ||
87 | # define I2S_SF_MCLKDIV_3 (0x13<<I2S_SF_MCLKDIV_SHIFT) | ||
88 | # define I2S_SF_MCLKDIV_5 (0x12<<I2S_SF_MCLKDIV_SHIFT) | ||
89 | # define I2S_SF_MCLKDIV_14 (0x0E<<I2S_SF_MCLKDIV_SHIFT) | ||
90 | # define I2S_SF_MCLKDIV_OTHER(div) (((div/2-1)<<I2S_SF_MCLKDIV_SHIFT)&I2S_SF_MCLKDIV_MASK) | ||
91 | static inline int i2s_sf_mclkdiv(int div, int *out) | ||
92 | { | ||
93 | int d; | ||
94 | |||
95 | switch(div) { | ||
96 | case 1: *out |= I2S_SF_MCLKDIV_1; return 0; | ||
97 | case 3: *out |= I2S_SF_MCLKDIV_3; return 0; | ||
98 | case 5: *out |= I2S_SF_MCLKDIV_5; return 0; | ||
99 | case 14: *out |= I2S_SF_MCLKDIV_14; return 0; | ||
100 | default: | ||
101 | if (div%2) return -1; | ||
102 | d = div/2-1; | ||
103 | if (d == 0x14 || d == 0x13 || d == 0x12 || d == 0x0E) | ||
104 | return -1; | ||
105 | *out |= I2S_SF_MCLKDIV_OTHER(div); | ||
106 | return 0; | ||
107 | } | ||
108 | } | ||
109 | /* SClk is the clock that drives the i2s wire bus. Note that it is | ||
110 | * derived from the MClk above by taking only every 'divisor' tick | ||
111 | * of MClk. | ||
112 | */ | ||
113 | # define I2S_SF_SCLKDIV_SHIFT 20 | ||
114 | # define I2S_SF_SCLKDIV_MASK (0xF<<I2S_SF_SCLKDIV_SHIFT) | ||
115 | # define I2S_SF_SCLKDIV_1 (8<<I2S_SF_SCLKDIV_SHIFT) | ||
116 | # define I2S_SF_SCLKDIV_3 (9<<I2S_SF_SCLKDIV_SHIFT) | ||
117 | # define I2S_SF_SCLKDIV_OTHER(div) (((div/2-1)<<I2S_SF_SCLKDIV_SHIFT)&I2S_SF_SCLKDIV_MASK) | ||
118 | static inline int i2s_sf_sclkdiv(int div, int *out) | ||
119 | { | ||
120 | int d; | ||
121 | |||
122 | switch(div) { | ||
123 | case 1: *out |= I2S_SF_SCLKDIV_1; return 0; | ||
124 | case 3: *out |= I2S_SF_SCLKDIV_3; return 0; | ||
125 | default: | ||
126 | if (div%2) return -1; | ||
127 | d = div/2-1; | ||
128 | if (d == 8 || d == 9) return -1; | ||
129 | *out |= I2S_SF_SCLKDIV_OTHER(div); | ||
130 | return 0; | ||
131 | } | ||
132 | } | ||
133 | # define I2S_SF_SCLK_MASTER (1<<19) | ||
134 | /* serial format is the way the data is put to the i2s wire bus */ | ||
135 | # define I2S_SF_SERIAL_FORMAT_SHIFT 16 | ||
136 | # define I2S_SF_SERIAL_FORMAT_MASK (7<<I2S_SF_SERIAL_FORMAT_SHIFT) | ||
137 | # define I2S_SF_SERIAL_FORMAT_SONY (0<<I2S_SF_SERIAL_FORMAT_SHIFT) | ||
138 | # define I2S_SF_SERIAL_FORMAT_I2S_64X (1<<I2S_SF_SERIAL_FORMAT_SHIFT) | ||
139 | # define I2S_SF_SERIAL_FORMAT_I2S_32X (2<<I2S_SF_SERIAL_FORMAT_SHIFT) | ||
140 | # define I2S_SF_SERIAL_FORMAT_I2S_DAV (4<<I2S_SF_SERIAL_FORMAT_SHIFT) | ||
141 | # define I2S_SF_SERIAL_FORMAT_I2S_SILABS (5<<I2S_SF_SERIAL_FORMAT_SHIFT) | ||
142 | /* unknown */ | ||
143 | # define I2S_SF_EXT_SAMPLE_FREQ_INT_SHIFT 12 | ||
144 | # define I2S_SF_EXT_SAMPLE_FREQ_INT_MASK (0xF<<I2S_SF_SAMPLE_FREQ_INT_SHIFT) | ||
145 | /* probably gives external frequency? */ | ||
146 | # define I2S_SF_EXT_SAMPLE_FREQ_MASK 0xFFF | ||
147 | |||
148 | /* used to send codec messages, but how isn't clear */ | ||
149 | #define I2S_REG_CODEC_MSG_OUT 0x20 | ||
150 | |||
151 | /* used to receive codec messages, but how isn't clear */ | ||
152 | #define I2S_REG_CODEC_MSG_IN 0x30 | ||
153 | |||
154 | /* frame count reg isn't clear to me yet, but probably useful */ | ||
155 | #define I2S_REG_FRAME_COUNT 0x40 | ||
156 | |||
157 | /* program to some value, and get interrupt if frame count reaches it */ | ||
158 | #define I2S_REG_FRAME_MATCH 0x50 | ||
159 | |||
160 | /* this register describes how the bus transfers data */ | ||
161 | #define I2S_REG_DATA_WORD_SIZES 0x60 | ||
162 | /* number of interleaved input channels */ | ||
163 | # define I2S_DWS_NUM_CHANNELS_IN_SHIFT 24 | ||
164 | # define I2S_DWS_NUM_CHANNELS_IN_MASK (0x1F<<I2S_DWS_NUM_CHANNELS_IN_SHIFT) | ||
165 | /* word size of input data */ | ||
166 | # define I2S_DWS_DATA_IN_SIZE_SHIFT 16 | ||
167 | # define I2S_DWS_DATA_IN_16BIT (0<<I2S_DWS_DATA_IN_SIZE_SHIFT) | ||
168 | # define I2S_DWS_DATA_IN_24BIT (3<<I2S_DWS_DATA_IN_SIZE_SHIFT) | ||
169 | /* number of interleaved output channels */ | ||
170 | # define I2S_DWS_NUM_CHANNELS_OUT_SHIFT 8 | ||
171 | # define I2S_DWS_NUM_CHANNELS_OUT_MASK (0x1F<<I2S_DWS_NUM_CHANNELS_OUT_SHIFT) | ||
172 | /* word size of output data */ | ||
173 | # define I2S_DWS_DATA_OUT_SIZE_SHIFT 0 | ||
174 | # define I2S_DWS_DATA_OUT_16BIT (0<<I2S_DWS_DATA_OUT_SIZE_SHIFT) | ||
175 | # define I2S_DWS_DATA_OUT_24BIT (3<<I2S_DWS_DATA_OUT_SIZE_SHIFT) | ||
176 | |||
177 | |||
178 | /* unknown */ | ||
179 | #define I2S_REG_PEAK_LEVEL_SEL 0x70 | ||
180 | |||
181 | /* unknown */ | ||
182 | #define I2S_REG_PEAK_LEVEL_IN0 0x80 | ||
183 | |||
184 | /* unknown */ | ||
185 | #define I2S_REG_PEAK_LEVEL_IN1 0x90 | ||
186 | |||
187 | #endif /* __I2SBUS_INTERFACE_H */ | ||
diff --git a/sound/aoa/soundbus/i2sbus/i2sbus-pcm.c b/sound/aoa/soundbus/i2sbus/i2sbus-pcm.c new file mode 100644 index 000000000000..3049015a04f1 --- /dev/null +++ b/sound/aoa/soundbus/i2sbus/i2sbus-pcm.c | |||
@@ -0,0 +1,1021 @@ | |||
1 | /* | ||
2 | * i2sbus driver -- pcm routines | ||
3 | * | ||
4 | * Copyright 2006 Johannes Berg <johannes@sipsolutions.net> | ||
5 | * | ||
6 | * GPL v2, can be found in COPYING. | ||
7 | */ | ||
8 | |||
9 | #include <asm/io.h> | ||
10 | #include <linux/delay.h> | ||
11 | /* So apparently there's a reason for requiring driver.h | ||
12 | * to be included first, even if I don't know it... */ | ||
13 | #include <sound/driver.h> | ||
14 | #include <sound/core.h> | ||
15 | #include <asm/macio.h> | ||
16 | #include <linux/pci.h> | ||
17 | #include "../soundbus.h" | ||
18 | #include "i2sbus.h" | ||
19 | |||
20 | static inline void get_pcm_info(struct i2sbus_dev *i2sdev, int in, | ||
21 | struct pcm_info **pi, struct pcm_info **other) | ||
22 | { | ||
23 | if (in) { | ||
24 | if (pi) | ||
25 | *pi = &i2sdev->in; | ||
26 | if (other) | ||
27 | *other = &i2sdev->out; | ||
28 | } else { | ||
29 | if (pi) | ||
30 | *pi = &i2sdev->out; | ||
31 | if (other) | ||
32 | *other = &i2sdev->in; | ||
33 | } | ||
34 | } | ||
35 | |||
36 | static int clock_and_divisors(int mclk, int sclk, int rate, int *out) | ||
37 | { | ||
38 | /* sclk must be derived from mclk! */ | ||
39 | if (mclk % sclk) | ||
40 | return -1; | ||
41 | /* derive sclk register value */ | ||
42 | if (i2s_sf_sclkdiv(mclk / sclk, out)) | ||
43 | return -1; | ||
44 | |||
45 | if (I2S_CLOCK_SPEED_18MHz % (rate * mclk) == 0) { | ||
46 | if (!i2s_sf_mclkdiv(I2S_CLOCK_SPEED_18MHz / (rate * mclk), out)) { | ||
47 | *out |= I2S_SF_CLOCK_SOURCE_18MHz; | ||
48 | return 0; | ||
49 | } | ||
50 | } | ||
51 | if (I2S_CLOCK_SPEED_45MHz % (rate * mclk) == 0) { | ||
52 | if (!i2s_sf_mclkdiv(I2S_CLOCK_SPEED_45MHz / (rate * mclk), out)) { | ||
53 | *out |= I2S_SF_CLOCK_SOURCE_45MHz; | ||
54 | return 0; | ||
55 | } | ||
56 | } | ||
57 | if (I2S_CLOCK_SPEED_49MHz % (rate * mclk) == 0) { | ||
58 | if (!i2s_sf_mclkdiv(I2S_CLOCK_SPEED_49MHz / (rate * mclk), out)) { | ||
59 | *out |= I2S_SF_CLOCK_SOURCE_49MHz; | ||
60 | return 0; | ||
61 | } | ||
62 | } | ||
63 | return -1; | ||
64 | } | ||
65 | |||
66 | #define CHECK_RATE(rate) \ | ||
67 | do { if (rates & SNDRV_PCM_RATE_ ##rate) { \ | ||
68 | int dummy; \ | ||
69 | if (clock_and_divisors(sysclock_factor, \ | ||
70 | bus_factor, rate, &dummy)) \ | ||
71 | rates &= ~SNDRV_PCM_RATE_ ##rate; \ | ||
72 | } } while (0) | ||
73 | |||
74 | static int i2sbus_pcm_open(struct i2sbus_dev *i2sdev, int in) | ||
75 | { | ||
76 | struct pcm_info *pi, *other; | ||
77 | struct soundbus_dev *sdev; | ||
78 | int masks_inited = 0, err; | ||
79 | struct codec_info_item *cii, *rev; | ||
80 | struct snd_pcm_hardware *hw; | ||
81 | u64 formats = 0; | ||
82 | unsigned int rates = 0; | ||
83 | struct transfer_info v; | ||
84 | int result = 0; | ||
85 | int bus_factor = 0, sysclock_factor = 0; | ||
86 | int found_this; | ||
87 | |||
88 | mutex_lock(&i2sdev->lock); | ||
89 | |||
90 | get_pcm_info(i2sdev, in, &pi, &other); | ||
91 | |||
92 | hw = &pi->substream->runtime->hw; | ||
93 | sdev = &i2sdev->sound; | ||
94 | |||
95 | if (pi->active) { | ||
96 | /* alsa messed up */ | ||
97 | result = -EBUSY; | ||
98 | goto out_unlock; | ||
99 | } | ||
100 | |||
101 | /* we now need to assign the hw */ | ||
102 | list_for_each_entry(cii, &sdev->codec_list, list) { | ||
103 | struct transfer_info *ti = cii->codec->transfers; | ||
104 | bus_factor = cii->codec->bus_factor; | ||
105 | sysclock_factor = cii->codec->sysclock_factor; | ||
106 | while (ti->formats && ti->rates) { | ||
107 | v = *ti; | ||
108 | if (ti->transfer_in == in | ||
109 | && cii->codec->usable(cii, ti, &v)) { | ||
110 | if (masks_inited) { | ||
111 | formats &= v.formats; | ||
112 | rates &= v.rates; | ||
113 | } else { | ||
114 | formats = v.formats; | ||
115 | rates = v.rates; | ||
116 | masks_inited = 1; | ||
117 | } | ||
118 | } | ||
119 | ti++; | ||
120 | } | ||
121 | } | ||
122 | if (!masks_inited || !bus_factor || !sysclock_factor) { | ||
123 | result = -ENODEV; | ||
124 | goto out_unlock; | ||
125 | } | ||
126 | /* bus dependent stuff */ | ||
127 | hw->info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | | ||
128 | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_RESUME; | ||
129 | |||
130 | CHECK_RATE(5512); | ||
131 | CHECK_RATE(8000); | ||
132 | CHECK_RATE(11025); | ||
133 | CHECK_RATE(16000); | ||
134 | CHECK_RATE(22050); | ||
135 | CHECK_RATE(32000); | ||
136 | CHECK_RATE(44100); | ||
137 | CHECK_RATE(48000); | ||
138 | CHECK_RATE(64000); | ||
139 | CHECK_RATE(88200); | ||
140 | CHECK_RATE(96000); | ||
141 | CHECK_RATE(176400); | ||
142 | CHECK_RATE(192000); | ||
143 | hw->rates = rates; | ||
144 | |||
145 | /* well. the codec might want 24 bits only, and we'll | ||
146 | * ever only transfer 24 bits, but they are top-aligned! | ||
147 | * So for alsa, we claim that we're doing full 32 bit | ||
148 | * while in reality we'll ignore the lower 8 bits of | ||
149 | * that when doing playback (they're transferred as 0 | ||
150 | * as far as I know, no codecs we have are 32-bit capable | ||
151 | * so I can't really test) and when doing recording we'll | ||
152 | * always have those lower 8 bits recorded as 0 */ | ||
153 | if (formats & SNDRV_PCM_FMTBIT_S24_BE) | ||
154 | formats |= SNDRV_PCM_FMTBIT_S32_BE; | ||
155 | if (formats & SNDRV_PCM_FMTBIT_U24_BE) | ||
156 | formats |= SNDRV_PCM_FMTBIT_U32_BE; | ||
157 | /* now mask off what we can support. I suppose we could | ||
158 | * also support S24_3LE and some similar formats, but I | ||
159 | * doubt there's a codec that would be able to use that, | ||
160 | * so we don't support it here. */ | ||
161 | hw->formats = formats & (SNDRV_PCM_FMTBIT_S16_BE | | ||
162 | SNDRV_PCM_FMTBIT_U16_BE | | ||
163 | SNDRV_PCM_FMTBIT_S32_BE | | ||
164 | SNDRV_PCM_FMTBIT_U32_BE); | ||
165 | |||
166 | /* we need to set the highest and lowest rate possible. | ||
167 | * These are the highest and lowest rates alsa can | ||
168 | * support properly in its bitfield. | ||
169 | * Below, we'll use that to restrict to the rate | ||
170 | * currently in use (if any). */ | ||
171 | hw->rate_min = 5512; | ||
172 | hw->rate_max = 192000; | ||
173 | /* if the other stream is active, then we can only | ||
174 | * support what it is currently using. | ||
175 | * FIXME: I lied. This comment is wrong. We can support | ||
176 | * anything that works with the same serial format, ie. | ||
177 | * when recording 24 bit sound we can well play 16 bit | ||
178 | * sound at the same time iff using the same transfer mode. | ||
179 | */ | ||
180 | if (other->active) { | ||
181 | /* FIXME: is this guaranteed by the alsa api? */ | ||
182 | hw->formats &= (1ULL << i2sdev->format); | ||
183 | /* see above, restrict rates to the one we already have */ | ||
184 | hw->rate_min = i2sdev->rate; | ||
185 | hw->rate_max = i2sdev->rate; | ||
186 | } | ||
187 | |||
188 | hw->channels_min = 2; | ||
189 | hw->channels_max = 2; | ||
190 | /* these are somewhat arbitrary */ | ||
191 | hw->buffer_bytes_max = 131072; | ||
192 | hw->period_bytes_min = 256; | ||
193 | hw->period_bytes_max = 16384; | ||
194 | hw->periods_min = 3; | ||
195 | hw->periods_max = MAX_DBDMA_COMMANDS; | ||
196 | list_for_each_entry(cii, &sdev->codec_list, list) { | ||
197 | if (cii->codec->open) { | ||
198 | err = cii->codec->open(cii, pi->substream); | ||
199 | if (err) { | ||
200 | result = err; | ||
201 | /* unwind */ | ||
202 | found_this = 0; | ||
203 | list_for_each_entry_reverse(rev, | ||
204 | &sdev->codec_list, list) { | ||
205 | if (found_this && rev->codec->close) { | ||
206 | rev->codec->close(rev, | ||
207 | pi->substream); | ||
208 | } | ||
209 | if (rev == cii) | ||
210 | found_this = 1; | ||
211 | } | ||
212 | goto out_unlock; | ||
213 | } | ||
214 | } | ||
215 | } | ||
216 | |||
217 | out_unlock: | ||
218 | mutex_unlock(&i2sdev->lock); | ||
219 | return result; | ||
220 | } | ||
221 | |||
222 | #undef CHECK_RATE | ||
223 | |||
224 | static int i2sbus_pcm_close(struct i2sbus_dev *i2sdev, int in) | ||
225 | { | ||
226 | struct codec_info_item *cii; | ||
227 | struct pcm_info *pi; | ||
228 | int err = 0, tmp; | ||
229 | |||
230 | mutex_lock(&i2sdev->lock); | ||
231 | |||
232 | get_pcm_info(i2sdev, in, &pi, NULL); | ||
233 | |||
234 | list_for_each_entry(cii, &i2sdev->sound.codec_list, list) { | ||
235 | if (cii->codec->close) { | ||
236 | tmp = cii->codec->close(cii, pi->substream); | ||
237 | if (tmp) | ||
238 | err = tmp; | ||
239 | } | ||
240 | } | ||
241 | |||
242 | pi->substream = NULL; | ||
243 | pi->active = 0; | ||
244 | mutex_unlock(&i2sdev->lock); | ||
245 | return err; | ||
246 | } | ||
247 | |||
248 | static int i2sbus_hw_params(struct snd_pcm_substream *substream, | ||
249 | struct snd_pcm_hw_params *params) | ||
250 | { | ||
251 | return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); | ||
252 | } | ||
253 | |||
254 | static int i2sbus_hw_free(struct snd_pcm_substream *substream) | ||
255 | { | ||
256 | snd_pcm_lib_free_pages(substream); | ||
257 | return 0; | ||
258 | } | ||
259 | |||
260 | static int i2sbus_pcm_prepare(struct i2sbus_dev *i2sdev, int in) | ||
261 | { | ||
262 | /* whee. Hard work now. The user has selected a bitrate | ||
263 | * and bit format, so now we have to program our | ||
264 | * I2S controller appropriately. */ | ||
265 | struct snd_pcm_runtime *runtime; | ||
266 | struct dbdma_cmd *command; | ||
267 | int i, periodsize; | ||
268 | dma_addr_t offset; | ||
269 | struct bus_info bi; | ||
270 | struct codec_info_item *cii; | ||
271 | int sfr = 0; /* serial format register */ | ||
272 | int dws = 0; /* data word sizes reg */ | ||
273 | int input_16bit; | ||
274 | struct pcm_info *pi, *other; | ||
275 | int cnt; | ||
276 | int result = 0; | ||
277 | |||
278 | mutex_lock(&i2sdev->lock); | ||
279 | |||
280 | get_pcm_info(i2sdev, in, &pi, &other); | ||
281 | |||
282 | if (pi->dbdma_ring.running) { | ||
283 | result = -EBUSY; | ||
284 | goto out_unlock; | ||
285 | } | ||
286 | |||
287 | runtime = pi->substream->runtime; | ||
288 | pi->active = 1; | ||
289 | if (other->active && | ||
290 | ((i2sdev->format != runtime->format) | ||
291 | || (i2sdev->rate != runtime->rate))) { | ||
292 | result = -EINVAL; | ||
293 | goto out_unlock; | ||
294 | } | ||
295 | |||
296 | i2sdev->format = runtime->format; | ||
297 | i2sdev->rate = runtime->rate; | ||
298 | |||
299 | periodsize = snd_pcm_lib_period_bytes(pi->substream); | ||
300 | pi->current_period = 0; | ||
301 | |||
302 | /* generate dbdma command ring first */ | ||
303 | command = pi->dbdma_ring.cmds; | ||
304 | offset = runtime->dma_addr; | ||
305 | for (i = 0; i < pi->substream->runtime->periods; | ||
306 | i++, command++, offset += periodsize) { | ||
307 | memset(command, 0, sizeof(struct dbdma_cmd)); | ||
308 | command->command = | ||
309 | cpu_to_le16((in ? INPUT_MORE : OUTPUT_MORE) | INTR_ALWAYS); | ||
310 | command->phy_addr = cpu_to_le32(offset); | ||
311 | command->req_count = cpu_to_le16(periodsize); | ||
312 | command->xfer_status = cpu_to_le16(0); | ||
313 | } | ||
314 | /* last one branches back to first */ | ||
315 | command--; | ||
316 | command->command |= cpu_to_le16(BR_ALWAYS); | ||
317 | command->cmd_dep = cpu_to_le32(pi->dbdma_ring.bus_cmd_start); | ||
318 | |||
319 | /* ok, let's set the serial format and stuff */ | ||
320 | switch (runtime->format) { | ||
321 | /* 16 bit formats */ | ||
322 | case SNDRV_PCM_FORMAT_S16_BE: | ||
323 | case SNDRV_PCM_FORMAT_U16_BE: | ||
324 | /* FIXME: if we add different bus factors we need to | ||
325 | * do more here!! */ | ||
326 | bi.bus_factor = 0; | ||
327 | list_for_each_entry(cii, &i2sdev->sound.codec_list, list) { | ||
328 | bi.bus_factor = cii->codec->bus_factor; | ||
329 | break; | ||
330 | } | ||
331 | if (!bi.bus_factor) { | ||
332 | result = -ENODEV; | ||
333 | goto out_unlock; | ||
334 | } | ||
335 | input_16bit = 1; | ||
336 | break; | ||
337 | case SNDRV_PCM_FORMAT_S32_BE: | ||
338 | case SNDRV_PCM_FORMAT_U32_BE: | ||
339 | /* force 64x bus speed, otherwise the data cannot be | ||
340 | * transferred quickly enough! */ | ||
341 | bi.bus_factor = 64; | ||
342 | input_16bit = 0; | ||
343 | break; | ||
344 | default: | ||
345 | result = -EINVAL; | ||
346 | goto out_unlock; | ||
347 | } | ||
348 | /* we assume all sysclocks are the same! */ | ||
349 | list_for_each_entry(cii, &i2sdev->sound.codec_list, list) { | ||
350 | bi.sysclock_factor = cii->codec->sysclock_factor; | ||
351 | break; | ||
352 | } | ||
353 | |||
354 | if (clock_and_divisors(bi.sysclock_factor, | ||
355 | bi.bus_factor, | ||
356 | runtime->rate, | ||
357 | &sfr) < 0) { | ||
358 | result = -EINVAL; | ||
359 | goto out_unlock; | ||
360 | } | ||
361 | switch (bi.bus_factor) { | ||
362 | case 32: | ||
363 | sfr |= I2S_SF_SERIAL_FORMAT_I2S_32X; | ||
364 | break; | ||
365 | case 64: | ||
366 | sfr |= I2S_SF_SERIAL_FORMAT_I2S_64X; | ||
367 | break; | ||
368 | } | ||
369 | /* FIXME: THIS ASSUMES MASTER ALL THE TIME */ | ||
370 | sfr |= I2S_SF_SCLK_MASTER; | ||
371 | |||
372 | list_for_each_entry(cii, &i2sdev->sound.codec_list, list) { | ||
373 | int err = 0; | ||
374 | if (cii->codec->prepare) | ||
375 | err = cii->codec->prepare(cii, &bi, pi->substream); | ||
376 | if (err) { | ||
377 | result = err; | ||
378 | goto out_unlock; | ||
379 | } | ||
380 | } | ||
381 | /* codecs are fine with it, so set our clocks */ | ||
382 | if (input_16bit) | ||
383 | dws = (2 << I2S_DWS_NUM_CHANNELS_IN_SHIFT) | | ||
384 | (2 << I2S_DWS_NUM_CHANNELS_OUT_SHIFT) | | ||
385 | I2S_DWS_DATA_IN_16BIT | I2S_DWS_DATA_OUT_16BIT; | ||
386 | else | ||
387 | dws = (2 << I2S_DWS_NUM_CHANNELS_IN_SHIFT) | | ||
388 | (2 << I2S_DWS_NUM_CHANNELS_OUT_SHIFT) | | ||
389 | I2S_DWS_DATA_IN_24BIT | I2S_DWS_DATA_OUT_24BIT; | ||
390 | |||
391 | /* early exit if already programmed correctly */ | ||
392 | /* not locking these is fine since we touch them only in this function */ | ||
393 | if (in_le32(&i2sdev->intfregs->serial_format) == sfr | ||
394 | && in_le32(&i2sdev->intfregs->data_word_sizes) == dws) | ||
395 | goto out_unlock; | ||
396 | |||
397 | /* let's notify the codecs about clocks going away. | ||
398 | * For now we only do mastering on the i2s cell... */ | ||
399 | list_for_each_entry(cii, &i2sdev->sound.codec_list, list) | ||
400 | if (cii->codec->switch_clock) | ||
401 | cii->codec->switch_clock(cii, CLOCK_SWITCH_PREPARE_SLAVE); | ||
402 | |||
403 | i2sbus_control_enable(i2sdev->control, i2sdev); | ||
404 | i2sbus_control_cell(i2sdev->control, i2sdev, 1); | ||
405 | |||
406 | out_le32(&i2sdev->intfregs->intr_ctl, I2S_PENDING_CLOCKS_STOPPED); | ||
407 | |||
408 | i2sbus_control_clock(i2sdev->control, i2sdev, 0); | ||
409 | |||
410 | msleep(1); | ||
411 | |||
412 | /* wait for clock stopped. This can apparently take a while... */ | ||
413 | cnt = 100; | ||
414 | while (cnt-- && | ||
415 | !(in_le32(&i2sdev->intfregs->intr_ctl) & I2S_PENDING_CLOCKS_STOPPED)) { | ||
416 | msleep(5); | ||
417 | } | ||
418 | out_le32(&i2sdev->intfregs->intr_ctl, I2S_PENDING_CLOCKS_STOPPED); | ||
419 | |||
420 | /* not locking these is fine since we touch them only in this function */ | ||
421 | out_le32(&i2sdev->intfregs->serial_format, sfr); | ||
422 | out_le32(&i2sdev->intfregs->data_word_sizes, dws); | ||
423 | |||
424 | i2sbus_control_enable(i2sdev->control, i2sdev); | ||
425 | i2sbus_control_cell(i2sdev->control, i2sdev, 1); | ||
426 | i2sbus_control_clock(i2sdev->control, i2sdev, 1); | ||
427 | msleep(1); | ||
428 | |||
429 | list_for_each_entry(cii, &i2sdev->sound.codec_list, list) | ||
430 | if (cii->codec->switch_clock) | ||
431 | cii->codec->switch_clock(cii, CLOCK_SWITCH_SLAVE); | ||
432 | |||
433 | out_unlock: | ||
434 | mutex_unlock(&i2sdev->lock); | ||
435 | return result; | ||
436 | } | ||
437 | |||
438 | static struct dbdma_cmd STOP_CMD = { | ||
439 | .command = __constant_cpu_to_le16(DBDMA_STOP), | ||
440 | }; | ||
441 | |||
442 | static int i2sbus_pcm_trigger(struct i2sbus_dev *i2sdev, int in, int cmd) | ||
443 | { | ||
444 | struct codec_info_item *cii; | ||
445 | struct pcm_info *pi; | ||
446 | int timeout; | ||
447 | struct dbdma_cmd tmp; | ||
448 | int result = 0; | ||
449 | unsigned long flags; | ||
450 | |||
451 | spin_lock_irqsave(&i2sdev->low_lock, flags); | ||
452 | |||
453 | get_pcm_info(i2sdev, in, &pi, NULL); | ||
454 | |||
455 | switch (cmd) { | ||
456 | case SNDRV_PCM_TRIGGER_START: | ||
457 | case SNDRV_PCM_TRIGGER_RESUME: | ||
458 | if (pi->dbdma_ring.running) { | ||
459 | result = -EALREADY; | ||
460 | goto out_unlock; | ||
461 | } | ||
462 | list_for_each_entry(cii, &i2sdev->sound.codec_list, list) | ||
463 | if (cii->codec->start) | ||
464 | cii->codec->start(cii, pi->substream); | ||
465 | pi->dbdma_ring.running = 1; | ||
466 | |||
467 | /* reset dma engine */ | ||
468 | out_le32(&pi->dbdma->control, | ||
469 | 0 | (RUN | PAUSE | FLUSH | WAKE) << 16); | ||
470 | timeout = 100; | ||
471 | while (in_le32(&pi->dbdma->status) & RUN && timeout--) | ||
472 | udelay(1); | ||
473 | if (timeout <= 0) { | ||
474 | printk(KERN_ERR | ||
475 | "i2sbus: error waiting for dma reset\n"); | ||
476 | result = -ENXIO; | ||
477 | goto out_unlock; | ||
478 | } | ||
479 | |||
480 | /* write dma command buffer address to the dbdma chip */ | ||
481 | out_le32(&pi->dbdma->cmdptr, pi->dbdma_ring.bus_cmd_start); | ||
482 | /* post PCI write */ | ||
483 | mb(); | ||
484 | (void)in_le32(&pi->dbdma->status); | ||
485 | |||
486 | /* change first command to STOP */ | ||
487 | tmp = *pi->dbdma_ring.cmds; | ||
488 | *pi->dbdma_ring.cmds = STOP_CMD; | ||
489 | |||
490 | /* set running state, remember that the first command is STOP */ | ||
491 | out_le32(&pi->dbdma->control, RUN | (RUN << 16)); | ||
492 | timeout = 100; | ||
493 | /* wait for STOP to be executed */ | ||
494 | while (in_le32(&pi->dbdma->status) & ACTIVE && timeout--) | ||
495 | udelay(1); | ||
496 | if (timeout <= 0) { | ||
497 | printk(KERN_ERR "i2sbus: error waiting for dma stop\n"); | ||
498 | result = -ENXIO; | ||
499 | goto out_unlock; | ||
500 | } | ||
501 | /* again, write dma command buffer address to the dbdma chip, | ||
502 | * this time of the first real command */ | ||
503 | *pi->dbdma_ring.cmds = tmp; | ||
504 | out_le32(&pi->dbdma->cmdptr, pi->dbdma_ring.bus_cmd_start); | ||
505 | /* post write */ | ||
506 | mb(); | ||
507 | (void)in_le32(&pi->dbdma->status); | ||
508 | |||
509 | /* reset dma engine again */ | ||
510 | out_le32(&pi->dbdma->control, | ||
511 | 0 | (RUN | PAUSE | FLUSH | WAKE) << 16); | ||
512 | timeout = 100; | ||
513 | while (in_le32(&pi->dbdma->status) & RUN && timeout--) | ||
514 | udelay(1); | ||
515 | if (timeout <= 0) { | ||
516 | printk(KERN_ERR | ||
517 | "i2sbus: error waiting for dma reset\n"); | ||
518 | result = -ENXIO; | ||
519 | goto out_unlock; | ||
520 | } | ||
521 | |||
522 | /* wake up the chip with the next descriptor */ | ||
523 | out_le32(&pi->dbdma->control, | ||
524 | (RUN | WAKE) | ((RUN | WAKE) << 16)); | ||
525 | /* get the frame count */ | ||
526 | pi->frame_count = in_le32(&i2sdev->intfregs->frame_count); | ||
527 | |||
528 | /* off you go! */ | ||
529 | break; | ||
530 | case SNDRV_PCM_TRIGGER_STOP: | ||
531 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
532 | if (!pi->dbdma_ring.running) { | ||
533 | result = -EALREADY; | ||
534 | goto out_unlock; | ||
535 | } | ||
536 | |||
537 | /* turn off all relevant bits */ | ||
538 | out_le32(&pi->dbdma->control, | ||
539 | (RUN | WAKE | FLUSH | PAUSE) << 16); | ||
540 | { | ||
541 | /* FIXME: move to own function */ | ||
542 | int timeout = 5000; | ||
543 | while ((in_le32(&pi->dbdma->status) & RUN) | ||
544 | && --timeout > 0) | ||
545 | udelay(1); | ||
546 | if (!timeout) | ||
547 | printk(KERN_ERR | ||
548 | "i2sbus: timed out turning " | ||
549 | "off dbdma engine!\n"); | ||
550 | } | ||
551 | |||
552 | pi->dbdma_ring.running = 0; | ||
553 | list_for_each_entry(cii, &i2sdev->sound.codec_list, list) | ||
554 | if (cii->codec->stop) | ||
555 | cii->codec->stop(cii, pi->substream); | ||
556 | break; | ||
557 | default: | ||
558 | result = -EINVAL; | ||
559 | goto out_unlock; | ||
560 | } | ||
561 | |||
562 | out_unlock: | ||
563 | spin_unlock_irqrestore(&i2sdev->low_lock, flags); | ||
564 | return result; | ||
565 | } | ||
566 | |||
567 | static snd_pcm_uframes_t i2sbus_pcm_pointer(struct i2sbus_dev *i2sdev, int in) | ||
568 | { | ||
569 | struct pcm_info *pi; | ||
570 | u32 fc; | ||
571 | |||
572 | get_pcm_info(i2sdev, in, &pi, NULL); | ||
573 | |||
574 | fc = in_le32(&i2sdev->intfregs->frame_count); | ||
575 | fc = fc - pi->frame_count; | ||
576 | |||
577 | return (bytes_to_frames(pi->substream->runtime, | ||
578 | pi->current_period * | ||
579 | snd_pcm_lib_period_bytes(pi->substream)) | ||
580 | + fc) % pi->substream->runtime->buffer_size; | ||
581 | } | ||
582 | |||
583 | static inline void handle_interrupt(struct i2sbus_dev *i2sdev, int in) | ||
584 | { | ||
585 | struct pcm_info *pi; | ||
586 | u32 fc; | ||
587 | u32 delta; | ||
588 | |||
589 | spin_lock(&i2sdev->low_lock); | ||
590 | get_pcm_info(i2sdev, in, &pi, NULL); | ||
591 | |||
592 | if (!pi->dbdma_ring.running) { | ||
593 | /* there was still an interrupt pending | ||
594 | * while we stopped. or maybe another | ||
595 | * processor (not the one that was stopping | ||
596 | * the DMA engine) was spinning above | ||
597 | * waiting for the lock. */ | ||
598 | goto out_unlock; | ||
599 | } | ||
600 | |||
601 | fc = in_le32(&i2sdev->intfregs->frame_count); | ||
602 | /* a counter overflow does not change the calculation. */ | ||
603 | delta = fc - pi->frame_count; | ||
604 | |||
605 | /* update current_period */ | ||
606 | while (delta >= pi->substream->runtime->period_size) { | ||
607 | pi->current_period++; | ||
608 | delta = delta - pi->substream->runtime->period_size; | ||
609 | } | ||
610 | |||
611 | if (unlikely(delta)) { | ||
612 | /* Some interrupt came late, so check the dbdma. | ||
613 | * This special case exists to syncronize the frame_count with | ||
614 | * the dbdma transfer, but is hit every once in a while. */ | ||
615 | int period; | ||
616 | |||
617 | period = (in_le32(&pi->dbdma->cmdptr) | ||
618 | - pi->dbdma_ring.bus_cmd_start) | ||
619 | / sizeof(struct dbdma_cmd); | ||
620 | pi->current_period = pi->current_period | ||
621 | % pi->substream->runtime->periods; | ||
622 | |||
623 | while (pi->current_period != period) { | ||
624 | pi->current_period++; | ||
625 | pi->current_period %= pi->substream->runtime->periods; | ||
626 | /* Set delta to zero, as the frame_count value is too | ||
627 | * high (otherwise the code path will not be executed). | ||
628 | * This corrects the fact that the frame_count is too | ||
629 | * low at the beginning due to buffering. */ | ||
630 | delta = 0; | ||
631 | } | ||
632 | } | ||
633 | |||
634 | pi->frame_count = fc - delta; | ||
635 | pi->current_period %= pi->substream->runtime->periods; | ||
636 | |||
637 | spin_unlock(&i2sdev->low_lock); | ||
638 | /* may call _trigger again, hence needs to be unlocked */ | ||
639 | snd_pcm_period_elapsed(pi->substream); | ||
640 | return; | ||
641 | out_unlock: | ||
642 | spin_unlock(&i2sdev->low_lock); | ||
643 | } | ||
644 | |||
645 | irqreturn_t i2sbus_tx_intr(int irq, void *devid, struct pt_regs *regs) | ||
646 | { | ||
647 | handle_interrupt((struct i2sbus_dev *)devid, 0); | ||
648 | return IRQ_HANDLED; | ||
649 | } | ||
650 | |||
651 | irqreturn_t i2sbus_rx_intr(int irq, void *devid, struct pt_regs * regs) | ||
652 | { | ||
653 | handle_interrupt((struct i2sbus_dev *)devid, 1); | ||
654 | return IRQ_HANDLED; | ||
655 | } | ||
656 | |||
657 | static int i2sbus_playback_open(struct snd_pcm_substream *substream) | ||
658 | { | ||
659 | struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream); | ||
660 | |||
661 | if (!i2sdev) | ||
662 | return -EINVAL; | ||
663 | i2sdev->out.substream = substream; | ||
664 | return i2sbus_pcm_open(i2sdev, 0); | ||
665 | } | ||
666 | |||
667 | static int i2sbus_playback_close(struct snd_pcm_substream *substream) | ||
668 | { | ||
669 | struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream); | ||
670 | int err; | ||
671 | |||
672 | if (!i2sdev) | ||
673 | return -EINVAL; | ||
674 | if (i2sdev->out.substream != substream) | ||
675 | return -EINVAL; | ||
676 | err = i2sbus_pcm_close(i2sdev, 0); | ||
677 | if (!err) | ||
678 | i2sdev->out.substream = NULL; | ||
679 | return err; | ||
680 | } | ||
681 | |||
682 | static int i2sbus_playback_prepare(struct snd_pcm_substream *substream) | ||
683 | { | ||
684 | struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream); | ||
685 | |||
686 | if (!i2sdev) | ||
687 | return -EINVAL; | ||
688 | if (i2sdev->out.substream != substream) | ||
689 | return -EINVAL; | ||
690 | return i2sbus_pcm_prepare(i2sdev, 0); | ||
691 | } | ||
692 | |||
693 | static int i2sbus_playback_trigger(struct snd_pcm_substream *substream, int cmd) | ||
694 | { | ||
695 | struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream); | ||
696 | |||
697 | if (!i2sdev) | ||
698 | return -EINVAL; | ||
699 | if (i2sdev->out.substream != substream) | ||
700 | return -EINVAL; | ||
701 | return i2sbus_pcm_trigger(i2sdev, 0, cmd); | ||
702 | } | ||
703 | |||
704 | static snd_pcm_uframes_t i2sbus_playback_pointer(struct snd_pcm_substream | ||
705 | *substream) | ||
706 | { | ||
707 | struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream); | ||
708 | |||
709 | if (!i2sdev) | ||
710 | return -EINVAL; | ||
711 | if (i2sdev->out.substream != substream) | ||
712 | return 0; | ||
713 | return i2sbus_pcm_pointer(i2sdev, 0); | ||
714 | } | ||
715 | |||
716 | static struct snd_pcm_ops i2sbus_playback_ops = { | ||
717 | .open = i2sbus_playback_open, | ||
718 | .close = i2sbus_playback_close, | ||
719 | .ioctl = snd_pcm_lib_ioctl, | ||
720 | .hw_params = i2sbus_hw_params, | ||
721 | .hw_free = i2sbus_hw_free, | ||
722 | .prepare = i2sbus_playback_prepare, | ||
723 | .trigger = i2sbus_playback_trigger, | ||
724 | .pointer = i2sbus_playback_pointer, | ||
725 | }; | ||
726 | |||
727 | static int i2sbus_record_open(struct snd_pcm_substream *substream) | ||
728 | { | ||
729 | struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream); | ||
730 | |||
731 | if (!i2sdev) | ||
732 | return -EINVAL; | ||
733 | i2sdev->in.substream = substream; | ||
734 | return i2sbus_pcm_open(i2sdev, 1); | ||
735 | } | ||
736 | |||
737 | static int i2sbus_record_close(struct snd_pcm_substream *substream) | ||
738 | { | ||
739 | struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream); | ||
740 | int err; | ||
741 | |||
742 | if (!i2sdev) | ||
743 | return -EINVAL; | ||
744 | if (i2sdev->in.substream != substream) | ||
745 | return -EINVAL; | ||
746 | err = i2sbus_pcm_close(i2sdev, 1); | ||
747 | if (!err) | ||
748 | i2sdev->in.substream = NULL; | ||
749 | return err; | ||
750 | } | ||
751 | |||
752 | static int i2sbus_record_prepare(struct snd_pcm_substream *substream) | ||
753 | { | ||
754 | struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream); | ||
755 | |||
756 | if (!i2sdev) | ||
757 | return -EINVAL; | ||
758 | if (i2sdev->in.substream != substream) | ||
759 | return -EINVAL; | ||
760 | return i2sbus_pcm_prepare(i2sdev, 1); | ||
761 | } | ||
762 | |||
763 | static int i2sbus_record_trigger(struct snd_pcm_substream *substream, int cmd) | ||
764 | { | ||
765 | struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream); | ||
766 | |||
767 | if (!i2sdev) | ||
768 | return -EINVAL; | ||
769 | if (i2sdev->in.substream != substream) | ||
770 | return -EINVAL; | ||
771 | return i2sbus_pcm_trigger(i2sdev, 1, cmd); | ||
772 | } | ||
773 | |||
774 | static snd_pcm_uframes_t i2sbus_record_pointer(struct snd_pcm_substream | ||
775 | *substream) | ||
776 | { | ||
777 | struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream); | ||
778 | |||
779 | if (!i2sdev) | ||
780 | return -EINVAL; | ||
781 | if (i2sdev->in.substream != substream) | ||
782 | return 0; | ||
783 | return i2sbus_pcm_pointer(i2sdev, 1); | ||
784 | } | ||
785 | |||
786 | static struct snd_pcm_ops i2sbus_record_ops = { | ||
787 | .open = i2sbus_record_open, | ||
788 | .close = i2sbus_record_close, | ||
789 | .ioctl = snd_pcm_lib_ioctl, | ||
790 | .hw_params = i2sbus_hw_params, | ||
791 | .hw_free = i2sbus_hw_free, | ||
792 | .prepare = i2sbus_record_prepare, | ||
793 | .trigger = i2sbus_record_trigger, | ||
794 | .pointer = i2sbus_record_pointer, | ||
795 | }; | ||
796 | |||
797 | static void i2sbus_private_free(struct snd_pcm *pcm) | ||
798 | { | ||
799 | struct i2sbus_dev *i2sdev = snd_pcm_chip(pcm); | ||
800 | struct codec_info_item *p, *tmp; | ||
801 | |||
802 | i2sdev->sound.pcm = NULL; | ||
803 | i2sdev->out.created = 0; | ||
804 | i2sdev->in.created = 0; | ||
805 | list_for_each_entry_safe(p, tmp, &i2sdev->sound.codec_list, list) { | ||
806 | printk(KERN_ERR "i2sbus: a codec didn't unregister!\n"); | ||
807 | list_del(&p->list); | ||
808 | module_put(p->codec->owner); | ||
809 | kfree(p); | ||
810 | } | ||
811 | soundbus_dev_put(&i2sdev->sound); | ||
812 | module_put(THIS_MODULE); | ||
813 | } | ||
814 | |||
815 | /* FIXME: this function needs an error handling strategy with labels */ | ||
816 | int | ||
817 | i2sbus_attach_codec(struct soundbus_dev *dev, struct snd_card *card, | ||
818 | struct codec_info *ci, void *data) | ||
819 | { | ||
820 | int err, in = 0, out = 0; | ||
821 | struct transfer_info *tmp; | ||
822 | struct i2sbus_dev *i2sdev = soundbus_dev_to_i2sbus_dev(dev); | ||
823 | struct codec_info_item *cii; | ||
824 | |||
825 | if (!dev->pcmname || dev->pcmid == -1) { | ||
826 | printk(KERN_ERR "i2sbus: pcm name and id must be set!\n"); | ||
827 | return -EINVAL; | ||
828 | } | ||
829 | |||
830 | list_for_each_entry(cii, &dev->codec_list, list) { | ||
831 | if (cii->codec_data == data) | ||
832 | return -EALREADY; | ||
833 | } | ||
834 | |||
835 | if (!ci->transfers || !ci->transfers->formats | ||
836 | || !ci->transfers->rates || !ci->usable) | ||
837 | return -EINVAL; | ||
838 | |||
839 | /* we currently code the i2s transfer on the clock, and support only | ||
840 | * 32 and 64 */ | ||
841 | if (ci->bus_factor != 32 && ci->bus_factor != 64) | ||
842 | return -EINVAL; | ||
843 | |||
844 | /* If you want to fix this, you need to keep track of what transport infos | ||
845 | * are to be used, which codecs they belong to, and then fix all the | ||
846 | * sysclock/busclock stuff above to depend on which is usable */ | ||
847 | list_for_each_entry(cii, &dev->codec_list, list) { | ||
848 | if (cii->codec->sysclock_factor != ci->sysclock_factor) { | ||
849 | printk(KERN_DEBUG | ||
850 | "cannot yet handle multiple different sysclocks!\n"); | ||
851 | return -EINVAL; | ||
852 | } | ||
853 | if (cii->codec->bus_factor != ci->bus_factor) { | ||
854 | printk(KERN_DEBUG | ||
855 | "cannot yet handle multiple different bus clocks!\n"); | ||
856 | return -EINVAL; | ||
857 | } | ||
858 | } | ||
859 | |||
860 | tmp = ci->transfers; | ||
861 | while (tmp->formats && tmp->rates) { | ||
862 | if (tmp->transfer_in) | ||
863 | in = 1; | ||
864 | else | ||
865 | out = 1; | ||
866 | tmp++; | ||
867 | } | ||
868 | |||
869 | cii = kzalloc(sizeof(struct codec_info_item), GFP_KERNEL); | ||
870 | if (!cii) { | ||
871 | printk(KERN_DEBUG "i2sbus: failed to allocate cii\n"); | ||
872 | return -ENOMEM; | ||
873 | } | ||
874 | |||
875 | /* use the private data to point to the codec info */ | ||
876 | cii->sdev = soundbus_dev_get(dev); | ||
877 | cii->codec = ci; | ||
878 | cii->codec_data = data; | ||
879 | |||
880 | if (!cii->sdev) { | ||
881 | printk(KERN_DEBUG | ||
882 | "i2sbus: failed to get soundbus dev reference\n"); | ||
883 | kfree(cii); | ||
884 | return -ENODEV; | ||
885 | } | ||
886 | |||
887 | if (!try_module_get(THIS_MODULE)) { | ||
888 | printk(KERN_DEBUG "i2sbus: failed to get module reference!\n"); | ||
889 | soundbus_dev_put(dev); | ||
890 | kfree(cii); | ||
891 | return -EBUSY; | ||
892 | } | ||
893 | |||
894 | if (!try_module_get(ci->owner)) { | ||
895 | printk(KERN_DEBUG | ||
896 | "i2sbus: failed to get module reference to codec owner!\n"); | ||
897 | module_put(THIS_MODULE); | ||
898 | soundbus_dev_put(dev); | ||
899 | kfree(cii); | ||
900 | return -EBUSY; | ||
901 | } | ||
902 | |||
903 | if (!dev->pcm) { | ||
904 | err = snd_pcm_new(card, | ||
905 | dev->pcmname, | ||
906 | dev->pcmid, | ||
907 | 0, | ||
908 | 0, | ||
909 | &dev->pcm); | ||
910 | if (err) { | ||
911 | printk(KERN_DEBUG "i2sbus: failed to create pcm\n"); | ||
912 | kfree(cii); | ||
913 | module_put(ci->owner); | ||
914 | soundbus_dev_put(dev); | ||
915 | module_put(THIS_MODULE); | ||
916 | return err; | ||
917 | } | ||
918 | } | ||
919 | |||
920 | /* ALSA yet again sucks. | ||
921 | * If it is ever fixed, remove this line. See below. */ | ||
922 | out = in = 1; | ||
923 | |||
924 | if (!i2sdev->out.created && out) { | ||
925 | if (dev->pcm->card != card) { | ||
926 | /* eh? */ | ||
927 | printk(KERN_ERR | ||
928 | "Can't attach same bus to different cards!\n"); | ||
929 | module_put(ci->owner); | ||
930 | kfree(cii); | ||
931 | soundbus_dev_put(dev); | ||
932 | module_put(THIS_MODULE); | ||
933 | return -EINVAL; | ||
934 | } | ||
935 | if ((err = | ||
936 | snd_pcm_new_stream(dev->pcm, SNDRV_PCM_STREAM_PLAYBACK, 1))) { | ||
937 | module_put(ci->owner); | ||
938 | kfree(cii); | ||
939 | soundbus_dev_put(dev); | ||
940 | module_put(THIS_MODULE); | ||
941 | return err; | ||
942 | } | ||
943 | snd_pcm_set_ops(dev->pcm, SNDRV_PCM_STREAM_PLAYBACK, | ||
944 | &i2sbus_playback_ops); | ||
945 | i2sdev->out.created = 1; | ||
946 | } | ||
947 | |||
948 | if (!i2sdev->in.created && in) { | ||
949 | if (dev->pcm->card != card) { | ||
950 | printk(KERN_ERR | ||
951 | "Can't attach same bus to different cards!\n"); | ||
952 | module_put(ci->owner); | ||
953 | kfree(cii); | ||
954 | soundbus_dev_put(dev); | ||
955 | module_put(THIS_MODULE); | ||
956 | return -EINVAL; | ||
957 | } | ||
958 | if ((err = | ||
959 | snd_pcm_new_stream(dev->pcm, SNDRV_PCM_STREAM_CAPTURE, 1))) { | ||
960 | module_put(ci->owner); | ||
961 | kfree(cii); | ||
962 | soundbus_dev_put(dev); | ||
963 | module_put(THIS_MODULE); | ||
964 | return err; | ||
965 | } | ||
966 | snd_pcm_set_ops(dev->pcm, SNDRV_PCM_STREAM_CAPTURE, | ||
967 | &i2sbus_record_ops); | ||
968 | i2sdev->in.created = 1; | ||
969 | } | ||
970 | |||
971 | /* so we have to register the pcm after adding any substream | ||
972 | * to it because alsa doesn't create the devices for the | ||
973 | * substreams when we add them later. | ||
974 | * Therefore, force in and out on both busses (above) and | ||
975 | * register the pcm now instead of just after creating it. | ||
976 | */ | ||
977 | err = snd_device_register(card, dev->pcm); | ||
978 | if (err) { | ||
979 | printk(KERN_ERR "i2sbus: error registering new pcm\n"); | ||
980 | module_put(ci->owner); | ||
981 | kfree(cii); | ||
982 | soundbus_dev_put(dev); | ||
983 | module_put(THIS_MODULE); | ||
984 | return err; | ||
985 | } | ||
986 | /* no errors any more, so let's add this to our list */ | ||
987 | list_add(&cii->list, &dev->codec_list); | ||
988 | |||
989 | dev->pcm->private_data = i2sdev; | ||
990 | dev->pcm->private_free = i2sbus_private_free; | ||
991 | |||
992 | /* well, we really should support scatter/gather DMA */ | ||
993 | snd_pcm_lib_preallocate_pages_for_all( | ||
994 | dev->pcm, SNDRV_DMA_TYPE_DEV, | ||
995 | snd_dma_pci_data(macio_get_pci_dev(i2sdev->macio)), | ||
996 | 64 * 1024, 64 * 1024); | ||
997 | |||
998 | return 0; | ||
999 | } | ||
1000 | |||
1001 | void i2sbus_detach_codec(struct soundbus_dev *dev, void *data) | ||
1002 | { | ||
1003 | struct codec_info_item *cii = NULL, *i; | ||
1004 | |||
1005 | list_for_each_entry(i, &dev->codec_list, list) { | ||
1006 | if (i->codec_data == data) { | ||
1007 | cii = i; | ||
1008 | break; | ||
1009 | } | ||
1010 | } | ||
1011 | if (cii) { | ||
1012 | list_del(&cii->list); | ||
1013 | module_put(cii->codec->owner); | ||
1014 | kfree(cii); | ||
1015 | } | ||
1016 | /* no more codecs, but still a pcm? */ | ||
1017 | if (list_empty(&dev->codec_list) && dev->pcm) { | ||
1018 | /* the actual cleanup is done by the callback above! */ | ||
1019 | snd_device_free(dev->pcm->card, dev->pcm); | ||
1020 | } | ||
1021 | } | ||
diff --git a/sound/aoa/soundbus/i2sbus/i2sbus.h b/sound/aoa/soundbus/i2sbus/i2sbus.h new file mode 100644 index 000000000000..cfa5162e3b0f --- /dev/null +++ b/sound/aoa/soundbus/i2sbus/i2sbus.h | |||
@@ -0,0 +1,112 @@ | |||
1 | /* | ||
2 | * i2sbus driver -- private definitions | ||
3 | * | ||
4 | * Copyright 2006 Johannes Berg <johannes@sipsolutions.net> | ||
5 | * | ||
6 | * GPL v2, can be found in COPYING. | ||
7 | */ | ||
8 | #ifndef __I2SBUS_H | ||
9 | #define __I2SBUS_H | ||
10 | #include <asm/dbdma.h> | ||
11 | #include <linux/interrupt.h> | ||
12 | #include <sound/pcm.h> | ||
13 | #include <linux/spinlock.h> | ||
14 | #include <linux/mutex.h> | ||
15 | #include <asm/prom.h> | ||
16 | #include "i2sbus-interface.h" | ||
17 | #include "i2sbus-control.h" | ||
18 | #include "../soundbus.h" | ||
19 | |||
20 | struct i2sbus_control { | ||
21 | volatile struct i2s_control_regs __iomem *controlregs; | ||
22 | struct resource rsrc; | ||
23 | struct list_head list; | ||
24 | }; | ||
25 | |||
26 | #define MAX_DBDMA_COMMANDS 32 | ||
27 | |||
28 | struct dbdma_command_mem { | ||
29 | dma_addr_t bus_addr; | ||
30 | dma_addr_t bus_cmd_start; | ||
31 | struct dbdma_cmd *cmds; | ||
32 | void *space; | ||
33 | int size; | ||
34 | u32 running:1; | ||
35 | }; | ||
36 | |||
37 | struct pcm_info { | ||
38 | u32 created:1, /* has this direction been created with alsa? */ | ||
39 | active:1; /* is this stream active? */ | ||
40 | /* runtime information */ | ||
41 | struct snd_pcm_substream *substream; | ||
42 | int current_period; | ||
43 | u32 frame_count; | ||
44 | struct dbdma_command_mem dbdma_ring; | ||
45 | volatile struct dbdma_regs __iomem *dbdma; | ||
46 | }; | ||
47 | |||
48 | struct i2sbus_dev { | ||
49 | struct soundbus_dev sound; | ||
50 | struct macio_dev *macio; | ||
51 | struct i2sbus_control *control; | ||
52 | volatile struct i2s_interface_regs __iomem *intfregs; | ||
53 | |||
54 | struct resource resources[3]; | ||
55 | struct resource *allocated_resource[3]; | ||
56 | int interrupts[3]; | ||
57 | char rnames[3][32]; | ||
58 | |||
59 | /* info about currently active substreams */ | ||
60 | struct pcm_info out, in; | ||
61 | snd_pcm_format_t format; | ||
62 | unsigned int rate; | ||
63 | |||
64 | /* list for a single controller */ | ||
65 | struct list_head item; | ||
66 | /* number of bus on controller */ | ||
67 | int bus_number; | ||
68 | /* for use by control layer */ | ||
69 | struct pmf_function *enable, | ||
70 | *cell_enable, | ||
71 | *cell_disable, | ||
72 | *clock_enable, | ||
73 | *clock_disable; | ||
74 | |||
75 | /* locks */ | ||
76 | /* spinlock for low-level interrupt locking */ | ||
77 | spinlock_t low_lock; | ||
78 | /* mutex for high-level consistency */ | ||
79 | struct mutex lock; | ||
80 | }; | ||
81 | |||
82 | #define soundbus_dev_to_i2sbus_dev(sdev) \ | ||
83 | container_of(sdev, struct i2sbus_dev, sound) | ||
84 | |||
85 | /* pcm specific functions */ | ||
86 | extern int | ||
87 | i2sbus_attach_codec(struct soundbus_dev *dev, struct snd_card *card, | ||
88 | struct codec_info *ci, void *data); | ||
89 | extern void | ||
90 | i2sbus_detach_codec(struct soundbus_dev *dev, void *data); | ||
91 | extern irqreturn_t | ||
92 | i2sbus_tx_intr(int irq, void *devid, struct pt_regs *regs); | ||
93 | extern irqreturn_t | ||
94 | i2sbus_rx_intr(int irq, void *devid, struct pt_regs *regs); | ||
95 | |||
96 | /* control specific functions */ | ||
97 | extern int i2sbus_control_init(struct macio_dev* dev, | ||
98 | struct i2sbus_control **c); | ||
99 | extern void i2sbus_control_destroy(struct i2sbus_control *c); | ||
100 | extern int i2sbus_control_add_dev(struct i2sbus_control *c, | ||
101 | struct i2sbus_dev *i2sdev); | ||
102 | extern void i2sbus_control_remove_dev(struct i2sbus_control *c, | ||
103 | struct i2sbus_dev *i2sdev); | ||
104 | extern int i2sbus_control_enable(struct i2sbus_control *c, | ||
105 | struct i2sbus_dev *i2sdev); | ||
106 | extern int i2sbus_control_cell(struct i2sbus_control *c, | ||
107 | struct i2sbus_dev *i2sdev, | ||
108 | int enable); | ||
109 | extern int i2sbus_control_clock(struct i2sbus_control *c, | ||
110 | struct i2sbus_dev *i2sdev, | ||
111 | int enable); | ||
112 | #endif /* __I2SBUS_H */ | ||
diff --git a/sound/aoa/soundbus/soundbus.h b/sound/aoa/soundbus/soundbus.h new file mode 100644 index 000000000000..5c27297835d7 --- /dev/null +++ b/sound/aoa/soundbus/soundbus.h | |||
@@ -0,0 +1,202 @@ | |||
1 | /* | ||
2 | * soundbus generic definitions | ||
3 | * | ||
4 | * Copyright 2006 Johannes Berg <johannes@sipsolutions.net> | ||
5 | * | ||
6 | * GPL v2, can be found in COPYING. | ||
7 | */ | ||
8 | #ifndef __SOUNDBUS_H | ||
9 | #define __SOUNDBUS_H | ||
10 | |||
11 | #include <asm/of_device.h> | ||
12 | #include <sound/pcm.h> | ||
13 | #include <linux/list.h> | ||
14 | |||
15 | |||
16 | /* When switching from master to slave or the other way around, | ||
17 | * you don't want to have the codec chip acting as clock source | ||
18 | * while the bus still is. | ||
19 | * More importantly, while switch from slave to master, you need | ||
20 | * to turn off the chip's master function first, but then there's | ||
21 | * no clock for a while and other chips might reset, so we notify | ||
22 | * their drivers after having switched. | ||
23 | * The constants here are codec-point of view, so when we switch | ||
24 | * the soundbus to master we tell the codec we're going to switch | ||
25 | * and give it CLOCK_SWITCH_PREPARE_SLAVE! | ||
26 | */ | ||
27 | enum clock_switch { | ||
28 | CLOCK_SWITCH_PREPARE_SLAVE, | ||
29 | CLOCK_SWITCH_PREPARE_MASTER, | ||
30 | CLOCK_SWITCH_SLAVE, | ||
31 | CLOCK_SWITCH_MASTER, | ||
32 | CLOCK_SWITCH_NOTIFY, | ||
33 | }; | ||
34 | |||
35 | /* information on a transfer the codec can take */ | ||
36 | struct transfer_info { | ||
37 | u64 formats; /* SNDRV_PCM_FMTBIT_* */ | ||
38 | unsigned int rates; /* SNDRV_PCM_RATE_* */ | ||
39 | /* flags */ | ||
40 | u32 transfer_in:1, /* input = 1, output = 0 */ | ||
41 | must_be_clock_source:1; | ||
42 | /* for codecs to distinguish among their TIs */ | ||
43 | int tag; | ||
44 | }; | ||
45 | |||
46 | struct codec_info_item { | ||
47 | struct codec_info *codec; | ||
48 | void *codec_data; | ||
49 | struct soundbus_dev *sdev; | ||
50 | /* internal, to be used by the soundbus provider */ | ||
51 | struct list_head list; | ||
52 | }; | ||
53 | |||
54 | /* for prepare, where the codecs need to know | ||
55 | * what we're going to drive the bus with */ | ||
56 | struct bus_info { | ||
57 | /* see below */ | ||
58 | int sysclock_factor; | ||
59 | int bus_factor; | ||
60 | }; | ||
61 | |||
62 | /* information on the codec itself, plus function pointers */ | ||
63 | struct codec_info { | ||
64 | /* the module this lives in */ | ||
65 | struct module *owner; | ||
66 | |||
67 | /* supported transfer possibilities, array terminated by | ||
68 | * formats or rates being 0. */ | ||
69 | struct transfer_info *transfers; | ||
70 | |||
71 | /* Master clock speed factor | ||
72 | * to be used (master clock speed = sysclock_factor * sampling freq) | ||
73 | * Unused if the soundbus provider has no such notion. | ||
74 | */ | ||
75 | int sysclock_factor; | ||
76 | |||
77 | /* Bus factor, bus clock speed = bus_factor * sampling freq) | ||
78 | * Unused if the soundbus provider has no such notion. | ||
79 | */ | ||
80 | int bus_factor; | ||
81 | |||
82 | /* operations */ | ||
83 | /* clock switching, see above */ | ||
84 | int (*switch_clock)(struct codec_info_item *cii, | ||
85 | enum clock_switch clock); | ||
86 | |||
87 | /* called for each transfer_info when the user | ||
88 | * opens the pcm device to determine what the | ||
89 | * hardware can support at this point in time. | ||
90 | * That can depend on other user-switchable controls. | ||
91 | * Return 1 if usable, 0 if not. | ||
92 | * out points to another instance of a transfer_info | ||
93 | * which is initialised to the values in *ti, and | ||
94 | * it's format and rate values can be modified by | ||
95 | * the callback if it is necessary to further restrict | ||
96 | * the formats that can be used at the moment, for | ||
97 | * example when one codec has multiple logical codec | ||
98 | * info structs for multiple inputs. | ||
99 | */ | ||
100 | int (*usable)(struct codec_info_item *cii, | ||
101 | struct transfer_info *ti, | ||
102 | struct transfer_info *out); | ||
103 | |||
104 | /* called when pcm stream is opened, probably not implemented | ||
105 | * most of the time since it isn't too useful */ | ||
106 | int (*open)(struct codec_info_item *cii, | ||
107 | struct snd_pcm_substream *substream); | ||
108 | |||
109 | /* called when the pcm stream is closed, at this point | ||
110 | * the user choices can all be unlocked (see below) */ | ||
111 | int (*close)(struct codec_info_item *cii, | ||
112 | struct snd_pcm_substream *substream); | ||
113 | |||
114 | /* if the codec must forbid some user choices because | ||
115 | * they are not valid with the substream/transfer info, | ||
116 | * it must do so here. Example: no digital output for | ||
117 | * incompatible framerate, say 8KHz, on Onyx. | ||
118 | * If the selected stuff in the substream is NOT | ||
119 | * compatible, you have to reject this call! */ | ||
120 | int (*prepare)(struct codec_info_item *cii, | ||
121 | struct bus_info *bi, | ||
122 | struct snd_pcm_substream *substream); | ||
123 | |||
124 | /* start() is called before data is pushed to the codec. | ||
125 | * Note that start() must be atomic! */ | ||
126 | int (*start)(struct codec_info_item *cii, | ||
127 | struct snd_pcm_substream *substream); | ||
128 | |||
129 | /* stop() is called after data is no longer pushed to the codec. | ||
130 | * Note that stop() must be atomic! */ | ||
131 | int (*stop)(struct codec_info_item *cii, | ||
132 | struct snd_pcm_substream *substream); | ||
133 | |||
134 | int (*suspend)(struct codec_info_item *cii, pm_message_t state); | ||
135 | int (*resume)(struct codec_info_item *cii); | ||
136 | }; | ||
137 | |||
138 | /* information on a soundbus device */ | ||
139 | struct soundbus_dev { | ||
140 | /* the bus it belongs to */ | ||
141 | struct list_head onbuslist; | ||
142 | |||
143 | /* the of device it represents */ | ||
144 | struct of_device ofdev; | ||
145 | |||
146 | /* what modules go by */ | ||
147 | char modalias[32]; | ||
148 | |||
149 | /* These fields must be before attach_codec can be called. | ||
150 | * They should be set by the owner of the alsa card object | ||
151 | * that is needed, and whoever sets them must make sure | ||
152 | * that they are unique within that alsa card object. */ | ||
153 | char *pcmname; | ||
154 | int pcmid; | ||
155 | |||
156 | /* this is assigned by the soundbus provider in attach_codec */ | ||
157 | struct snd_pcm *pcm; | ||
158 | |||
159 | /* operations */ | ||
160 | /* attach a codec to this soundbus, give the alsa | ||
161 | * card object the PCMs for this soundbus should be in. | ||
162 | * The 'data' pointer must be unique, it is used as the | ||
163 | * key for detach_codec(). */ | ||
164 | int (*attach_codec)(struct soundbus_dev *dev, struct snd_card *card, | ||
165 | struct codec_info *ci, void *data); | ||
166 | void (*detach_codec)(struct soundbus_dev *dev, void *data); | ||
167 | /* TODO: suspend/resume */ | ||
168 | |||
169 | /* private for the soundbus provider */ | ||
170 | struct list_head codec_list; | ||
171 | u32 have_out:1, have_in:1; | ||
172 | }; | ||
173 | #define to_soundbus_device(d) container_of(d, struct soundbus_dev, ofdev.dev) | ||
174 | #define of_to_soundbus_device(d) container_of(d, struct soundbus_dev, ofdev) | ||
175 | |||
176 | extern int soundbus_add_one(struct soundbus_dev *dev); | ||
177 | extern void soundbus_remove_one(struct soundbus_dev *dev); | ||
178 | |||
179 | extern struct soundbus_dev *soundbus_dev_get(struct soundbus_dev *dev); | ||
180 | extern void soundbus_dev_put(struct soundbus_dev *dev); | ||
181 | |||
182 | struct soundbus_driver { | ||
183 | char *name; | ||
184 | struct module *owner; | ||
185 | |||
186 | /* we don't implement any matching at all */ | ||
187 | |||
188 | int (*probe)(struct soundbus_dev* dev); | ||
189 | int (*remove)(struct soundbus_dev* dev); | ||
190 | |||
191 | int (*suspend)(struct soundbus_dev* dev, pm_message_t state); | ||
192 | int (*resume)(struct soundbus_dev* dev); | ||
193 | int (*shutdown)(struct soundbus_dev* dev); | ||
194 | |||
195 | struct device_driver driver; | ||
196 | }; | ||
197 | #define to_soundbus_driver(drv) container_of(drv,struct soundbus_driver, driver) | ||
198 | |||
199 | extern int soundbus_register_driver(struct soundbus_driver *drv); | ||
200 | extern void soundbus_unregister_driver(struct soundbus_driver *drv); | ||
201 | |||
202 | #endif /* __SOUNDBUS_H */ | ||
diff --git a/sound/aoa/soundbus/sysfs.c b/sound/aoa/soundbus/sysfs.c new file mode 100644 index 000000000000..d31f8146952a --- /dev/null +++ b/sound/aoa/soundbus/sysfs.c | |||
@@ -0,0 +1,43 @@ | |||
1 | #include <linux/config.h> | ||
2 | #include <linux/kernel.h> | ||
3 | #include <linux/stat.h> | ||
4 | /* FIX UP */ | ||
5 | #include "soundbus.h" | ||
6 | |||
7 | #define soundbus_config_of_attr(field, format_string) \ | ||
8 | static ssize_t \ | ||
9 | field##_show (struct device *dev, struct device_attribute *attr, \ | ||
10 | char *buf) \ | ||
11 | { \ | ||
12 | struct soundbus_dev *mdev = to_soundbus_device (dev); \ | ||
13 | return sprintf (buf, format_string, mdev->ofdev.node->field); \ | ||
14 | } | ||
15 | |||
16 | static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, | ||
17 | char *buf) | ||
18 | { | ||
19 | struct soundbus_dev *sdev = to_soundbus_device(dev); | ||
20 | struct of_device *of = &sdev->ofdev; | ||
21 | int length; | ||
22 | |||
23 | if (*sdev->modalias) { | ||
24 | strlcpy(buf, sdev->modalias, sizeof(sdev->modalias) + 1); | ||
25 | strcat(buf, "\n"); | ||
26 | length = strlen(buf); | ||
27 | } else { | ||
28 | length = sprintf(buf, "of:N%sT%s\n", | ||
29 | of->node->name, of->node->type); | ||
30 | } | ||
31 | |||
32 | return length; | ||
33 | } | ||
34 | |||
35 | soundbus_config_of_attr (name, "%s\n"); | ||
36 | soundbus_config_of_attr (type, "%s\n"); | ||
37 | |||
38 | struct device_attribute soundbus_dev_attrs[] = { | ||
39 | __ATTR_RO(name), | ||
40 | __ATTR_RO(type), | ||
41 | __ATTR_RO(modalias), | ||
42 | __ATTR_NULL | ||
43 | }; | ||