summaryrefslogtreecommitdiffstats
path: root/sound/aoa/core/snd-aoa-core.c
blob: 19fdae400687eef99eae88b1e1dd9730d91d7ec9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
/*
 * Apple Onboard Audio driver core
 *
 * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
 *
 * GPL v2, can be found in COPYING.
 */

#include <linux/init.h>
#include <linux/module.h>
#include <linux/list.h>
#include "../aoa.h"
#include "snd-aoa-alsa.h"

MODULE_DESCRIPTION("Apple Onboard Audio Sound Driver");
MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
MODULE_LICENSE("GPL");

/* We allow only one fabric. This simplifies things,
 * and more don't really make that much sense */
static struct aoa_fabric *fabric;
static LIST_HEAD(codec_list);

static int attach_codec_to_fabric(struct aoa_codec *c)
{
	int err;

	if (!try_module_get(c->owner))
		return -EBUSY;
	/* found_codec has to be assigned */
	err = -ENOENT;
	if (fabric->found_codec)
		err = fabric->found_codec(c);
	if (err) {
		module_put(c->owner);
		printk(KERN_ERR "snd-aoa: fabric didn't like codec %s\n",
				c->name);
		return err;
	}
	c->fabric = fabric;

	err = 0;
	if (c->init)
		err = c->init(c);
	if (err) {
		printk(KERN_ERR "snd-aoa: codec %s didn't init\n", c->name);
		c->fabric = NULL;
		if (fabric->remove_codec)
			fabric->remove_codec(c);
		module_put(c->owner);
		return err;
	}
	if (fabric->attached_codec)
		fabric->attached_codec(c);
	return 0;
}

int aoa_codec_register(struct aoa_codec *codec)
{
	int err = 0;

	/* if there's a fabric already, we can tell if we
	 * will want to have this codec, so propagate error
	 * through. Otherwise, this will happen later... */
	if (fabric)
		err = attach_codec_to_fabric(codec);
	if (!err)
		list_add(&codec->list, &codec_list);
	return err;
}
EXPORT_SYMBOL_GPL(aoa_codec_register);

void aoa_codec_unregister(struct aoa_codec *codec)
{
	list_del(&codec->list);
	if (codec->fabric && codec->exit)
		codec->exit(codec);
	if (fabric && fabric->remove_codec)
		fabric->remove_codec(codec);
	codec->fabric = NULL;
	module_put(codec->owner);
}
EXPORT_SYMBOL_GPL(aoa_codec_unregister);

int aoa_fabric_register(struct aoa_fabric *new_fabric, struct device *dev)
{
	struct aoa_codec *c;
	int err;

	/* allow querying for presence of fabric
	 * (i.e. do this test first!) */
	if (new_fabric == fabric) {
		err = -EALREADY;
		goto attach;
	}
	if (fabric)
		return -EEXIST;
	if (!new_fabric)
		return -EINVAL;

	err = aoa_alsa_init(new_fabric->name, new_fabric->owner, dev);
	if (err)
		return err;

	fabric = new_fabric;

 attach:
	list_for_each_entry(c, &codec_list, list) {
		if (c->fabric != fabric)
			attach_codec_to_fabric(c);
	}
	return err;
}
EXPORT_SYMBOL_GPL(aoa_fabric_register);

void aoa_fabric_unregister(struct aoa_fabric *old_fabric)
{
	struct aoa_codec *c;

	if (fabric != old_fabric)
		return;

	list_for_each_entry(c, &codec_list, list) {
		if (c->fabric)
			aoa_fabric_unlink_codec(c);
	}

	aoa_alsa_cleanup();

	fabric = NULL;
}
EXPORT_SYMBOL_GPL(aoa_fabric_unregister);

void aoa_fabric_unlink_codec(struct aoa_codec *codec)
{
	if (!codec->fabric) {
		printk(KERN_ERR "snd-aoa: fabric unassigned "
				"in aoa_fabric_unlink_codec\n");
		dump_stack();
		return;
	}
	if (codec->exit)
		codec->exit(codec);
	if (codec->fabric->remove_codec)
		codec->fabric->remove_codec(codec);
	codec->fabric = NULL;
	module_put(codec->owner);
}
EXPORT_SYMBOL_GPL(aoa_fabric_unlink_codec);

static int __init aoa_init(void)
{
	return 0;
}

static void __exit aoa_exit(void)
{
	aoa_alsa_cleanup();
}

module_init(aoa_init);
module_exit(aoa_exit);