Merge branch 'for-linus' of master.kernel.org:/pub/scm/linux/kernel/git/jikos/hid
[linux-2.6] / sound / oss / dmasound / tas_common.c
1 #include <linux/module.h>
2 #include <linux/slab.h>
3 #include <linux/proc_fs.h>
4 #include <linux/ioport.h>
5 #include <linux/sysctl.h>
6 #include <linux/types.h>
7 #include <linux/i2c.h>
8 #include <linux/init.h>
9 #include <linux/soundcard.h>
10 #include <asm/uaccess.h>
11 #include <asm/errno.h>
12 #include <asm/io.h>
13 #include <asm/prom.h>
14
15 #include "tas_common.h"
16
17 #define CALL0(proc)                                                             \
18         do {                                                                    \
19                 struct tas_data_t *self;                                        \
20                 if (!tas_client || driver_hooks == NULL)                        \
21                         return -1;                                              \
22                 self = dev_get_drvdata(&tas_client->dev);                       \
23                 if (driver_hooks->proc)                                         \
24                         return driver_hooks->proc(self);                        \
25                 else                                                            \
26                         return -EINVAL;                                         \
27         } while (0)
28
29 #define CALL(proc,arg...)                                                       \
30         do {                                                                    \
31                 struct tas_data_t *self;                                        \
32                 if (!tas_client || driver_hooks == NULL)                        \
33                         return -1;                                              \
34                 self = dev_get_drvdata(&tas_client->dev);                       \
35                 if (driver_hooks->proc)                                         \
36                         return driver_hooks->proc(self, ## arg);                \
37                 else                                                            \
38                         return -EINVAL;                                         \
39         } while (0)
40
41
42 static u8 tas_i2c_address = 0x34;
43 static struct i2c_client *tas_client;
44
45 static int tas_attach_adapter(struct i2c_adapter *);
46 static int tas_detach_client(struct i2c_client *);
47
48 struct i2c_driver tas_driver = {
49         .driver = {
50                 .name   = "tas",
51         },
52         .attach_adapter = tas_attach_adapter,
53         .detach_client  = tas_detach_client,
54 };
55
56 struct tas_driver_hooks_t *driver_hooks;
57
58 int
59 tas_register_driver(struct tas_driver_hooks_t *hooks)
60 {
61         driver_hooks = hooks;
62         return 0;
63 }
64
65 int
66 tas_get_mixer_level(int mixer, uint *level)
67 {
68         CALL(get_mixer_level,mixer,level);
69 }
70
71 int
72 tas_set_mixer_level(int mixer,uint level)
73 {
74         CALL(set_mixer_level,mixer,level);
75 }
76
77 int
78 tas_enter_sleep(void)
79 {
80         CALL0(enter_sleep);
81 }
82
83 int
84 tas_leave_sleep(void)
85 {
86         CALL0(leave_sleep);
87 }
88
89 int
90 tas_supported_mixers(void)
91 {
92         CALL0(supported_mixers);
93 }
94
95 int
96 tas_mixer_is_stereo(int mixer)
97 {
98         CALL(mixer_is_stereo,mixer);
99 }
100
101 int
102 tas_stereo_mixers(void)
103 {
104         CALL0(stereo_mixers);
105 }
106
107 int
108 tas_output_device_change(int device_id,int layout_id,int speaker_id)
109 {
110         CALL(output_device_change,device_id,layout_id,speaker_id);
111 }
112
113 int
114 tas_device_ioctl(u_int cmd, u_long arg)
115 {
116         CALL(device_ioctl,cmd,arg);
117 }
118
119 int
120 tas_post_init(void)
121 {
122         CALL0(post_init);
123 }
124
125 static int
126 tas_detect_client(struct i2c_adapter *adapter, int address)
127 {
128         static const char *client_name = "tas Digital Equalizer";
129         struct i2c_client *new_client;
130         int rc = -ENODEV;
131
132         if (!driver_hooks) {
133                 printk(KERN_ERR "tas_detect_client called with no hooks !\n");
134                 return -ENODEV;
135         }
136         
137         new_client = kzalloc(sizeof(*new_client), GFP_KERNEL);
138         if (!new_client)
139                 return -ENOMEM;
140
141         new_client->addr = address;
142         new_client->adapter = adapter;
143         new_client->driver = &tas_driver;
144         strlcpy(new_client->name, client_name, DEVICE_NAME_SIZE);
145
146         if (driver_hooks->init(new_client))
147                 goto bail;
148
149         /* Tell the i2c layer a new client has arrived */
150         if (i2c_attach_client(new_client)) {
151                 driver_hooks->uninit(dev_get_drvdata(&new_client->dev));
152                 goto bail;
153         }
154
155         tas_client = new_client;
156         return 0;
157  bail:
158         tas_client = NULL;
159         kfree(new_client);
160         return rc;
161 }
162
163 static int
164 tas_attach_adapter(struct i2c_adapter *adapter)
165 {
166         if (!strncmp(adapter->name, "mac-io", 6))
167                 return tas_detect_client(adapter, tas_i2c_address);
168         return 0;
169 }
170
171 static int
172 tas_detach_client(struct i2c_client *client)
173 {
174         if (client == tas_client) {
175                 driver_hooks->uninit(dev_get_drvdata(&client->dev));
176
177                 i2c_detach_client(client);
178                 kfree(client);
179         }
180         return 0;
181 }
182
183 void
184 tas_cleanup(void)
185 {
186         i2c_del_driver(&tas_driver);
187 }
188
189 int __init
190 tas_init(int driver_id, const char *driver_name)
191 {
192         const u32* paddr;
193         struct device_node *tas_node;
194
195         printk(KERN_INFO "tas driver [%s])\n", driver_name);
196
197 #ifndef CONFIG_I2C_POWERMAC
198         request_module("i2c-powermac");
199 #endif
200         tas_node = of_find_node_by_name("deq");
201         if (tas_node == NULL)
202                 return -ENODEV;
203         paddr = of_get_property(tas_node, "i2c-address", NULL);
204         if (paddr) {
205                 tas_i2c_address = (*paddr) >> 1;
206                 printk(KERN_INFO "using i2c address: 0x%x from device-tree\n",
207                                 tas_i2c_address);
208         } else    
209                 printk(KERN_INFO "using i2c address: 0x%x (default)\n",
210                                 tas_i2c_address);
211         of_node_put(tas_node);
212
213         return i2c_add_driver(&tas_driver);
214 }