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 | ||
108 | return 0; | |
109 | } | |
110 | EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_init); | |
111 | ||
112 | /** | |
113 | * snd_hdac_ext_bus_exit - clean up a HD-audio extended bus | |
114 | * @ebus: the pointer to extended bus object | |
115 | */ | |
116 | void snd_hdac_ext_bus_exit(struct hdac_ext_bus *ebus) | |
117 | { | |
118 | snd_hdac_bus_exit(&ebus->bus); | |
119 | WARN_ON(!list_empty(&ebus->hlink_list)); | |
120 | } | |
121 | EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_exit); | |
122 | ||
123 | static void default_release(struct device *dev) | |
124 | { | |
125 | snd_hdac_ext_bus_device_exit(container_of(dev, struct hdac_device, dev)); | |
126 | } | |
127 | ||
128 | /** | |
a512f561 | 129 | * snd_hdac_ext_bus_device_init - initialize the HDA extended codec base device |
dfe66a18 JK |
130 | * @ebus: hdac extended bus to attach to |
131 | * @addr: codec address | |
132 | * | |
133 | * Returns zero for success or a negative error code. | |
134 | */ | |
135 | int snd_hdac_ext_bus_device_init(struct hdac_ext_bus *ebus, int addr) | |
136 | { | |
a512f561 | 137 | struct hdac_ext_device *edev; |
dfe66a18 JK |
138 | struct hdac_device *hdev = NULL; |
139 | struct hdac_bus *bus = ebus_to_hbus(ebus); | |
140 | char name[15]; | |
141 | int ret; | |
142 | ||
e57690b1 | 143 | edev = kzalloc(sizeof(*edev), GFP_KERNEL); |
a512f561 | 144 | if (!edev) |
dfe66a18 | 145 | return -ENOMEM; |
a512f561 | 146 | hdev = &edev->hdac; |
94e9080c | 147 | edev->ebus = ebus; |
dfe66a18 JK |
148 | |
149 | snprintf(name, sizeof(name), "ehdaudio%dD%d", ebus->idx, addr); | |
150 | ||
151 | ret = snd_hdac_device_init(hdev, bus, name, addr); | |
152 | if (ret < 0) { | |
153 | dev_err(bus->dev, "device init failed for hdac device\n"); | |
154 | return ret; | |
155 | } | |
156 | hdev->type = HDA_DEV_ASOC; | |
157 | hdev->dev.release = default_release; | |
158 | ||
159 | ret = snd_hdac_device_register(hdev); | |
160 | if (ret) { | |
161 | dev_err(bus->dev, "failed to register hdac device\n"); | |
162 | snd_hdac_ext_bus_device_exit(hdev); | |
163 | return ret; | |
164 | } | |
a512f561 | 165 | |
dfe66a18 JK |
166 | return 0; |
167 | } | |
168 | EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_device_init); | |
169 | ||
170 | /** | |
171 | * snd_hdac_ext_bus_device_exit - clean up a HD-audio extended codec base device | |
172 | * @hdev: hdac device to clean up | |
173 | */ | |
174 | void snd_hdac_ext_bus_device_exit(struct hdac_device *hdev) | |
175 | { | |
a512f561 VK |
176 | struct hdac_ext_device *edev = to_ehdac_device(hdev); |
177 | ||
dfe66a18 | 178 | snd_hdac_device_exit(hdev); |
a512f561 | 179 | kfree(edev); |
dfe66a18 JK |
180 | } |
181 | EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_device_exit); | |
ee2d51b3 VK |
182 | |
183 | /** | |
184 | * snd_hdac_ext_bus_device_remove - remove HD-audio extended codec base devices | |
185 | * | |
186 | * @ebus: HD-audio extended bus | |
187 | */ | |
188 | void snd_hdac_ext_bus_device_remove(struct hdac_ext_bus *ebus) | |
189 | { | |
190 | struct hdac_device *codec, *__codec; | |
191 | /* | |
192 | * we need to remove all the codec devices objects created in the | |
193 | * snd_hdac_ext_bus_device_init | |
194 | */ | |
195 | list_for_each_entry_safe(codec, __codec, &ebus->bus.codec_list, list) { | |
196 | snd_hdac_device_unregister(codec); | |
197 | put_device(&codec->dev); | |
198 | } | |
199 | } | |
200 | EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_device_remove); | |
d51783c1 VK |
201 | #define dev_to_hdac(dev) (container_of((dev), \ |
202 | struct hdac_device, dev)) | |
203 | ||
204 | static inline struct hdac_ext_driver *get_edrv(struct device *dev) | |
205 | { | |
206 | struct hdac_driver *hdrv = drv_to_hdac_driver(dev->driver); | |
207 | struct hdac_ext_driver *edrv = to_ehdac_driver(hdrv); | |
208 | ||
209 | return edrv; | |
210 | } | |
211 | ||
212 | static inline struct hdac_ext_device *get_edev(struct device *dev) | |
213 | { | |
214 | struct hdac_device *hdev = dev_to_hdac_dev(dev); | |
215 | struct hdac_ext_device *edev = to_ehdac_device(hdev); | |
216 | ||
217 | return edev; | |
218 | } | |
219 | ||
220 | static int hda_ext_drv_probe(struct device *dev) | |
221 | { | |
222 | return (get_edrv(dev))->probe(get_edev(dev)); | |
223 | } | |
224 | ||
225 | static int hdac_ext_drv_remove(struct device *dev) | |
226 | { | |
227 | return (get_edrv(dev))->remove(get_edev(dev)); | |
228 | } | |
229 | ||
230 | static void hdac_ext_drv_shutdown(struct device *dev) | |
231 | { | |
232 | return (get_edrv(dev))->shutdown(get_edev(dev)); | |
233 | } | |
234 | ||
235 | /** | |
236 | * snd_hda_ext_driver_register - register a driver for ext hda devices | |
237 | * | |
238 | * @drv: ext hda driver structure | |
239 | */ | |
240 | int snd_hda_ext_driver_register(struct hdac_ext_driver *drv) | |
241 | { | |
242 | drv->hdac.type = HDA_DEV_ASOC; | |
243 | drv->hdac.driver.bus = &snd_hda_bus_type; | |
244 | /* we use default match */ | |
245 | ||
246 | if (drv->probe) | |
247 | drv->hdac.driver.probe = hda_ext_drv_probe; | |
248 | if (drv->remove) | |
249 | drv->hdac.driver.remove = hdac_ext_drv_remove; | |
250 | if (drv->shutdown) | |
251 | drv->hdac.driver.shutdown = hdac_ext_drv_shutdown; | |
252 | ||
253 | return driver_register(&drv->hdac.driver); | |
254 | } | |
255 | EXPORT_SYMBOL_GPL(snd_hda_ext_driver_register); | |
256 | ||
257 | /** | |
258 | * snd_hda_ext_driver_unregister - unregister a driver for ext hda devices | |
259 | * | |
260 | * @drv: ext hda driver structure | |
261 | */ | |
262 | void snd_hda_ext_driver_unregister(struct hdac_ext_driver *drv) | |
263 | { | |
264 | driver_unregister(&drv->hdac.driver); | |
265 | } | |
266 | EXPORT_SYMBOL_GPL(snd_hda_ext_driver_unregister); |