Commit | Line | Data |
---|---|---|
dfe66a18 JK |
1 | /* |
2 | * hdac-ext-bus.c - HD-audio extended core bus functions. | |
3 | * | |
4 | * Copyright (C) 2014-2015 Intel Corp | |
5 | * Author: Jeeja KP <jeeja.kp@intel.com> | |
6 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or modify | |
9 | * it under the terms of the GNU General Public License as published by | |
10 | * the Free Software Foundation; version 2 of the License. | |
11 | * | |
12 | * This program is distributed in the hope that it will be useful, but | |
13 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 | * General Public License for more details. | |
16 | * | |
17 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
18 | */ | |
19 | ||
20 | #include <linux/module.h> | |
21 | #include <linux/slab.h> | |
42f2bb1c | 22 | #include <linux/io.h> |
dfe66a18 JK |
23 | #include <sound/hdaudio_ext.h> |
24 | ||
25 | MODULE_DESCRIPTION("HDA extended core"); | |
26 | MODULE_LICENSE("GPL v2"); | |
27 | ||
99463b3a VK |
28 | static void hdac_ext_writel(u32 value, u32 __iomem *addr) |
29 | { | |
30 | writel(value, addr); | |
31 | } | |
32 | ||
33 | static u32 hdac_ext_readl(u32 __iomem *addr) | |
34 | { | |
35 | return readl(addr); | |
36 | } | |
37 | ||
38 | static void hdac_ext_writew(u16 value, u16 __iomem *addr) | |
39 | { | |
40 | writew(value, addr); | |
41 | } | |
42 | ||
43 | static u16 hdac_ext_readw(u16 __iomem *addr) | |
44 | { | |
45 | return readw(addr); | |
46 | } | |
47 | ||
48 | static void hdac_ext_writeb(u8 value, u8 __iomem *addr) | |
49 | { | |
50 | writeb(value, addr); | |
51 | } | |
52 | ||
53 | static u8 hdac_ext_readb(u8 __iomem *addr) | |
54 | { | |
55 | return readb(addr); | |
56 | } | |
57 | ||
58 | static int hdac_ext_dma_alloc_pages(struct hdac_bus *bus, int type, | |
59 | size_t size, struct snd_dma_buffer *buf) | |
60 | { | |
61 | return snd_dma_alloc_pages(type, bus->dev, size, buf); | |
62 | } | |
63 | ||
64 | static void hdac_ext_dma_free_pages(struct hdac_bus *bus, struct snd_dma_buffer *buf) | |
65 | { | |
66 | snd_dma_free_pages(buf); | |
67 | } | |
68 | ||
69 | static const struct hdac_io_ops hdac_ext_default_io = { | |
70 | .reg_writel = hdac_ext_writel, | |
71 | .reg_readl = hdac_ext_readl, | |
72 | .reg_writew = hdac_ext_writew, | |
73 | .reg_readw = hdac_ext_readw, | |
74 | .reg_writeb = hdac_ext_writeb, | |
75 | .reg_readb = hdac_ext_readb, | |
76 | .dma_alloc_pages = hdac_ext_dma_alloc_pages, | |
77 | .dma_free_pages = hdac_ext_dma_free_pages, | |
78 | }; | |
79 | ||
dfe66a18 JK |
80 | /** |
81 | * snd_hdac_ext_bus_init - initialize a HD-audio extended bus | |
82 | * @ebus: the pointer to extended bus object | |
83 | * @dev: device pointer | |
84 | * @ops: bus verb operators | |
99463b3a VK |
85 | * @io_ops: lowlevel I/O operators, can be NULL. If NULL core will use |
86 | * default ops | |
dfe66a18 JK |
87 | * |
88 | * Returns 0 if successful, or a negative error code. | |
89 | */ | |
90 | int snd_hdac_ext_bus_init(struct hdac_ext_bus *ebus, struct device *dev, | |
91 | const struct hdac_bus_ops *ops, | |
92 | const struct hdac_io_ops *io_ops) | |
93 | { | |
94 | int ret; | |
95 | static int idx; | |
96 | ||
99463b3a VK |
97 | /* check if io ops are provided, if not load the defaults */ |
98 | if (io_ops == NULL) | |
99 | io_ops = &hdac_ext_default_io; | |
100 | ||
dfe66a18 JK |
101 | ret = snd_hdac_bus_init(&ebus->bus, dev, ops, io_ops); |
102 | if (ret < 0) | |
103 | return ret; | |
104 | ||
105 | INIT_LIST_HEAD(&ebus->hlink_list); | |
106 | ebus->idx = idx++; | |
107 | ||
4446085d VK |
108 | mutex_init(&ebus->lock); |
109 | ebus->cmd_dma_state = true; | |
110 | ||
dfe66a18 JK |
111 | return 0; |
112 | } | |
113 | EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_init); | |
114 | ||
115 | /** | |
116 | * snd_hdac_ext_bus_exit - clean up a HD-audio extended bus | |
117 | * @ebus: the pointer to extended bus object | |
118 | */ | |
119 | void snd_hdac_ext_bus_exit(struct hdac_ext_bus *ebus) | |
120 | { | |
121 | snd_hdac_bus_exit(&ebus->bus); | |
122 | WARN_ON(!list_empty(&ebus->hlink_list)); | |
123 | } | |
124 | EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_exit); | |
125 | ||
126 | static void default_release(struct device *dev) | |
127 | { | |
128 | snd_hdac_ext_bus_device_exit(container_of(dev, struct hdac_device, dev)); | |
129 | } | |
130 | ||
131 | /** | |
a512f561 | 132 | * snd_hdac_ext_bus_device_init - initialize the HDA extended codec base device |
dfe66a18 JK |
133 | * @ebus: hdac extended bus to attach to |
134 | * @addr: codec address | |
135 | * | |
136 | * Returns zero for success or a negative error code. | |
137 | */ | |
138 | int snd_hdac_ext_bus_device_init(struct hdac_ext_bus *ebus, int addr) | |
139 | { | |
a512f561 | 140 | struct hdac_ext_device *edev; |
dfe66a18 JK |
141 | struct hdac_device *hdev = NULL; |
142 | struct hdac_bus *bus = ebus_to_hbus(ebus); | |
143 | char name[15]; | |
144 | int ret; | |
145 | ||
e57690b1 | 146 | edev = kzalloc(sizeof(*edev), GFP_KERNEL); |
a512f561 | 147 | if (!edev) |
dfe66a18 | 148 | return -ENOMEM; |
a512f561 | 149 | hdev = &edev->hdac; |
94e9080c | 150 | edev->ebus = ebus; |
dfe66a18 JK |
151 | |
152 | snprintf(name, sizeof(name), "ehdaudio%dD%d", ebus->idx, addr); | |
153 | ||
154 | ret = snd_hdac_device_init(hdev, bus, name, addr); | |
155 | if (ret < 0) { | |
156 | dev_err(bus->dev, "device init failed for hdac device\n"); | |
157 | return ret; | |
158 | } | |
159 | hdev->type = HDA_DEV_ASOC; | |
160 | hdev->dev.release = default_release; | |
161 | ||
162 | ret = snd_hdac_device_register(hdev); | |
163 | if (ret) { | |
164 | dev_err(bus->dev, "failed to register hdac device\n"); | |
165 | snd_hdac_ext_bus_device_exit(hdev); | |
166 | return ret; | |
167 | } | |
a512f561 | 168 | |
dfe66a18 JK |
169 | return 0; |
170 | } | |
171 | EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_device_init); | |
172 | ||
173 | /** | |
174 | * snd_hdac_ext_bus_device_exit - clean up a HD-audio extended codec base device | |
175 | * @hdev: hdac device to clean up | |
176 | */ | |
177 | void snd_hdac_ext_bus_device_exit(struct hdac_device *hdev) | |
178 | { | |
a512f561 VK |
179 | struct hdac_ext_device *edev = to_ehdac_device(hdev); |
180 | ||
dfe66a18 | 181 | snd_hdac_device_exit(hdev); |
a512f561 | 182 | kfree(edev); |
dfe66a18 JK |
183 | } |
184 | EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_device_exit); | |
ee2d51b3 VK |
185 | |
186 | /** | |
187 | * snd_hdac_ext_bus_device_remove - remove HD-audio extended codec base devices | |
188 | * | |
189 | * @ebus: HD-audio extended bus | |
190 | */ | |
191 | void snd_hdac_ext_bus_device_remove(struct hdac_ext_bus *ebus) | |
192 | { | |
193 | struct hdac_device *codec, *__codec; | |
194 | /* | |
195 | * we need to remove all the codec devices objects created in the | |
196 | * snd_hdac_ext_bus_device_init | |
197 | */ | |
198 | list_for_each_entry_safe(codec, __codec, &ebus->bus.codec_list, list) { | |
199 | snd_hdac_device_unregister(codec); | |
200 | put_device(&codec->dev); | |
201 | } | |
202 | } | |
203 | EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_device_remove); | |
d51783c1 VK |
204 | #define dev_to_hdac(dev) (container_of((dev), \ |
205 | struct hdac_device, dev)) | |
206 | ||
207 | static inline struct hdac_ext_driver *get_edrv(struct device *dev) | |
208 | { | |
209 | struct hdac_driver *hdrv = drv_to_hdac_driver(dev->driver); | |
210 | struct hdac_ext_driver *edrv = to_ehdac_driver(hdrv); | |
211 | ||
212 | return edrv; | |
213 | } | |
214 | ||
215 | static inline struct hdac_ext_device *get_edev(struct device *dev) | |
216 | { | |
217 | struct hdac_device *hdev = dev_to_hdac_dev(dev); | |
218 | struct hdac_ext_device *edev = to_ehdac_device(hdev); | |
219 | ||
220 | return edev; | |
221 | } | |
222 | ||
223 | static int hda_ext_drv_probe(struct device *dev) | |
224 | { | |
225 | return (get_edrv(dev))->probe(get_edev(dev)); | |
226 | } | |
227 | ||
228 | static int hdac_ext_drv_remove(struct device *dev) | |
229 | { | |
230 | return (get_edrv(dev))->remove(get_edev(dev)); | |
231 | } | |
232 | ||
233 | static void hdac_ext_drv_shutdown(struct device *dev) | |
234 | { | |
235 | return (get_edrv(dev))->shutdown(get_edev(dev)); | |
236 | } | |
237 | ||
238 | /** | |
239 | * snd_hda_ext_driver_register - register a driver for ext hda devices | |
240 | * | |
241 | * @drv: ext hda driver structure | |
242 | */ | |
243 | int snd_hda_ext_driver_register(struct hdac_ext_driver *drv) | |
244 | { | |
245 | drv->hdac.type = HDA_DEV_ASOC; | |
246 | drv->hdac.driver.bus = &snd_hda_bus_type; | |
247 | /* we use default match */ | |
248 | ||
249 | if (drv->probe) | |
250 | drv->hdac.driver.probe = hda_ext_drv_probe; | |
251 | if (drv->remove) | |
252 | drv->hdac.driver.remove = hdac_ext_drv_remove; | |
253 | if (drv->shutdown) | |
254 | drv->hdac.driver.shutdown = hdac_ext_drv_shutdown; | |
255 | ||
256 | return driver_register(&drv->hdac.driver); | |
257 | } | |
258 | EXPORT_SYMBOL_GPL(snd_hda_ext_driver_register); | |
259 | ||
260 | /** | |
261 | * snd_hda_ext_driver_unregister - unregister a driver for ext hda devices | |
262 | * | |
263 | * @drv: ext hda driver structure | |
264 | */ | |
265 | void snd_hda_ext_driver_unregister(struct hdac_ext_driver *drv) | |
266 | { | |
267 | driver_unregister(&drv->hdac.driver); | |
268 | } | |
269 | EXPORT_SYMBOL_GPL(snd_hda_ext_driver_unregister); |