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
42static u8 tas_i2c_address = 0x34;
43static struct i2c_client *tas_client;
44
45static int tas_attach_adapter(struct i2c_adapter *);
46static int tas_detach_client(struct i2c_client *);
47
48struct i2c_driver tas_driver = {
49	.driver = {
50		.name	= "tas",
51	},
52	.attach_adapter	= tas_attach_adapter,
53	.detach_client	= tas_detach_client,
54};
55
56struct tas_driver_hooks_t *driver_hooks;
57
58int
59tas_register_driver(struct tas_driver_hooks_t *hooks)
60{
61	driver_hooks = hooks;
62	return 0;
63}
64
65int
66tas_get_mixer_level(int mixer, uint *level)
67{
68	CALL(get_mixer_level,mixer,level);
69}
70
71int
72tas_set_mixer_level(int mixer,uint level)
73{
74	CALL(set_mixer_level,mixer,level);
75}
76
77int
78tas_enter_sleep(void)
79{
80	CALL0(enter_sleep);
81}
82
83int
84tas_leave_sleep(void)
85{
86	CALL0(leave_sleep);
87}
88
89int
90tas_supported_mixers(void)
91{
92	CALL0(supported_mixers);
93}
94
95int
96tas_mixer_is_stereo(int mixer)
97{
98	CALL(mixer_is_stereo,mixer);
99}
100
101int
102tas_stereo_mixers(void)
103{
104	CALL0(stereo_mixers);
105}
106
107int
108tas_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
113int
114tas_device_ioctl(u_int cmd, u_long arg)
115{
116	CALL(device_ioctl,cmd,arg);
117}
118
119int
120tas_post_init(void)
121{
122	CALL0(post_init);
123}
124
125static int
126tas_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
163static int
164tas_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
171static int
172tas_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
183void
184tas_cleanup(void)
185{
186	i2c_del_driver(&tas_driver);
187}
188
189int __init
190tas_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}
215