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; |
dfe66a18 JK |
147 | |
148 | snprintf(name, sizeof(name), "ehdaudio%dD%d", ebus->idx, addr); | |
149 | ||
150 | ret = snd_hdac_device_init(hdev, bus, name, addr); | |
151 | if (ret < 0) { | |
152 | dev_err(bus->dev, "device init failed for hdac device\n"); | |
153 | return ret; | |
154 | } | |
155 | hdev->type = HDA_DEV_ASOC; | |
156 | hdev->dev.release = default_release; | |
157 | ||
158 | ret = snd_hdac_device_register(hdev); | |
159 | if (ret) { | |
160 | dev_err(bus->dev, "failed to register hdac device\n"); | |
161 | snd_hdac_ext_bus_device_exit(hdev); | |
162 | return ret; | |
163 | } | |
a512f561 | 164 | |
dfe66a18 JK |
165 | return 0; |
166 | } | |
167 | EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_device_init); | |
168 | ||
169 | /** | |
170 | * snd_hdac_ext_bus_device_exit - clean up a HD-audio extended codec base device | |
171 | * @hdev: hdac device to clean up | |
172 | */ | |
173 | void snd_hdac_ext_bus_device_exit(struct hdac_device *hdev) | |
174 | { | |
a512f561 VK |
175 | struct hdac_ext_device *edev = to_ehdac_device(hdev); |
176 | ||
dfe66a18 | 177 | snd_hdac_device_exit(hdev); |
a512f561 | 178 | kfree(edev); |
dfe66a18 JK |
179 | } |
180 | EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_device_exit); | |
ee2d51b3 VK |
181 | |
182 | /** | |
183 | * snd_hdac_ext_bus_device_remove - remove HD-audio extended codec base devices | |
184 | * | |
185 | * @ebus: HD-audio extended bus | |
186 | */ | |
187 | void snd_hdac_ext_bus_device_remove(struct hdac_ext_bus *ebus) | |
188 | { | |
189 | struct hdac_device *codec, *__codec; | |
190 | /* | |
191 | * we need to remove all the codec devices objects created in the | |
192 | * snd_hdac_ext_bus_device_init | |
193 | */ | |
194 | list_for_each_entry_safe(codec, __codec, &ebus->bus.codec_list, list) { | |
195 | snd_hdac_device_unregister(codec); | |
196 | put_device(&codec->dev); | |
197 | } | |
198 | } | |
199 | EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_device_remove); | |
d51783c1 VK |
200 | #define dev_to_hdac(dev) (container_of((dev), \ |
201 | struct hdac_device, dev)) | |
202 | ||
203 | static inline struct hdac_ext_driver *get_edrv(struct device *dev) | |
204 | { | |
205 | struct hdac_driver *hdrv = drv_to_hdac_driver(dev->driver); | |
206 | struct hdac_ext_driver *edrv = to_ehdac_driver(hdrv); | |
207 | ||
208 | return edrv; | |
209 | } | |
210 | ||
211 | static inline struct hdac_ext_device *get_edev(struct device *dev) | |
212 | { | |
213 | struct hdac_device *hdev = dev_to_hdac_dev(dev); | |
214 | struct hdac_ext_device *edev = to_ehdac_device(hdev); | |
215 | ||
216 | return edev; | |
217 | } | |
218 | ||
219 | static int hda_ext_drv_probe(struct device *dev) | |
220 | { | |
221 | return (get_edrv(dev))->probe(get_edev(dev)); | |
222 | } | |
223 | ||
224 | static int hdac_ext_drv_remove(struct device *dev) | |
225 | { | |
226 | return (get_edrv(dev))->remove(get_edev(dev)); | |
227 | } | |
228 | ||
229 | static void hdac_ext_drv_shutdown(struct device *dev) | |
230 | { | |
231 | return (get_edrv(dev))->shutdown(get_edev(dev)); | |
232 | } | |
233 | ||
234 | /** | |
235 | * snd_hda_ext_driver_register - register a driver for ext hda devices | |
236 | * | |
237 | * @drv: ext hda driver structure | |
238 | */ | |
239 | int snd_hda_ext_driver_register(struct hdac_ext_driver *drv) | |
240 | { | |
241 | drv->hdac.type = HDA_DEV_ASOC; | |
242 | drv->hdac.driver.bus = &snd_hda_bus_type; | |
243 | /* we use default match */ | |
244 | ||
245 | if (drv->probe) | |
246 | drv->hdac.driver.probe = hda_ext_drv_probe; | |
247 | if (drv->remove) | |
248 | drv->hdac.driver.remove = hdac_ext_drv_remove; | |
249 | if (drv->shutdown) | |
250 | drv->hdac.driver.shutdown = hdac_ext_drv_shutdown; | |
251 | ||
252 | return driver_register(&drv->hdac.driver); | |
253 | } | |
254 | EXPORT_SYMBOL_GPL(snd_hda_ext_driver_register); | |
255 | ||
256 | /** | |
257 | * snd_hda_ext_driver_unregister - unregister a driver for ext hda devices | |
258 | * | |
259 | * @drv: ext hda driver structure | |
260 | */ | |
261 | void snd_hda_ext_driver_unregister(struct hdac_ext_driver *drv) | |
262 | { | |
263 | driver_unregister(&drv->hdac.driver); | |
264 | } | |
265 | EXPORT_SYMBOL_GPL(snd_hda_ext_driver_unregister); |