lib/list_sort: simplify and remove MAX_LIST_LENGTH_BITS
[sfrench/cifs-2.6.git] / drivers / misc / atmel-ssc.c
1 /*
2  * Atmel SSC driver
3  *
4  * Copyright (C) 2007 Atmel Corporation
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation.
9  */
10
11 #include <linux/platform_device.h>
12 #include <linux/list.h>
13 #include <linux/clk.h>
14 #include <linux/err.h>
15 #include <linux/io.h>
16 #include <linux/spinlock.h>
17 #include <linux/atmel-ssc.h>
18 #include <linux/slab.h>
19 #include <linux/module.h>
20
21 #include <linux/of.h>
22
23 #include "../../sound/soc/atmel/atmel_ssc_dai.h"
24
25 /* Serialize access to ssc_list and user count */
26 static DEFINE_SPINLOCK(user_lock);
27 static LIST_HEAD(ssc_list);
28
29 struct ssc_device *ssc_request(unsigned int ssc_num)
30 {
31         int ssc_valid = 0;
32         struct ssc_device *ssc;
33
34         spin_lock(&user_lock);
35         list_for_each_entry(ssc, &ssc_list, list) {
36                 if (ssc->pdev->dev.of_node) {
37                         if (of_alias_get_id(ssc->pdev->dev.of_node, "ssc")
38                                 == ssc_num) {
39                                 ssc->pdev->id = ssc_num;
40                                 ssc_valid = 1;
41                                 break;
42                         }
43                 } else if (ssc->pdev->id == ssc_num) {
44                         ssc_valid = 1;
45                         break;
46                 }
47         }
48
49         if (!ssc_valid) {
50                 spin_unlock(&user_lock);
51                 pr_err("ssc: ssc%d platform device is missing\n", ssc_num);
52                 return ERR_PTR(-ENODEV);
53         }
54
55         if (ssc->user) {
56                 spin_unlock(&user_lock);
57                 dev_dbg(&ssc->pdev->dev, "module busy\n");
58                 return ERR_PTR(-EBUSY);
59         }
60         ssc->user++;
61         spin_unlock(&user_lock);
62
63         clk_prepare(ssc->clk);
64
65         return ssc;
66 }
67 EXPORT_SYMBOL(ssc_request);
68
69 void ssc_free(struct ssc_device *ssc)
70 {
71         bool disable_clk = true;
72
73         spin_lock(&user_lock);
74         if (ssc->user)
75                 ssc->user--;
76         else {
77                 disable_clk = false;
78                 dev_dbg(&ssc->pdev->dev, "device already free\n");
79         }
80         spin_unlock(&user_lock);
81
82         if (disable_clk)
83                 clk_unprepare(ssc->clk);
84 }
85 EXPORT_SYMBOL(ssc_free);
86
87 static struct atmel_ssc_platform_data at91rm9200_config = {
88         .use_dma = 0,
89         .has_fslen_ext = 0,
90 };
91
92 static struct atmel_ssc_platform_data at91sam9rl_config = {
93         .use_dma = 0,
94         .has_fslen_ext = 1,
95 };
96
97 static struct atmel_ssc_platform_data at91sam9g45_config = {
98         .use_dma = 1,
99         .has_fslen_ext = 1,
100 };
101
102 static const struct platform_device_id atmel_ssc_devtypes[] = {
103         {
104                 .name = "at91rm9200_ssc",
105                 .driver_data = (unsigned long) &at91rm9200_config,
106         }, {
107                 .name = "at91sam9rl_ssc",
108                 .driver_data = (unsigned long) &at91sam9rl_config,
109         }, {
110                 .name = "at91sam9g45_ssc",
111                 .driver_data = (unsigned long) &at91sam9g45_config,
112         }, {
113                 /* sentinel */
114         }
115 };
116
117 #ifdef CONFIG_OF
118 static const struct of_device_id atmel_ssc_dt_ids[] = {
119         {
120                 .compatible = "atmel,at91rm9200-ssc",
121                 .data = &at91rm9200_config,
122         }, {
123                 .compatible = "atmel,at91sam9rl-ssc",
124                 .data = &at91sam9rl_config,
125         }, {
126                 .compatible = "atmel,at91sam9g45-ssc",
127                 .data = &at91sam9g45_config,
128         }, {
129                 /* sentinel */
130         }
131 };
132 MODULE_DEVICE_TABLE(of, atmel_ssc_dt_ids);
133 #endif
134
135 static inline const struct atmel_ssc_platform_data *
136         atmel_ssc_get_driver_data(struct platform_device *pdev)
137 {
138         if (pdev->dev.of_node) {
139                 const struct of_device_id *match;
140                 match = of_match_node(atmel_ssc_dt_ids, pdev->dev.of_node);
141                 if (match == NULL)
142                         return NULL;
143                 return match->data;
144         }
145
146         return (struct atmel_ssc_platform_data *)
147                 platform_get_device_id(pdev)->driver_data;
148 }
149
150 #ifdef CONFIG_SND_ATMEL_SOC_SSC
151 static int ssc_sound_dai_probe(struct ssc_device *ssc)
152 {
153         struct device_node *np = ssc->pdev->dev.of_node;
154         int ret;
155         int id;
156
157         ssc->sound_dai = false;
158
159         if (!of_property_read_bool(np, "#sound-dai-cells"))
160                 return 0;
161
162         id = of_alias_get_id(np, "ssc");
163         if (id < 0)
164                 return id;
165
166         ret = atmel_ssc_set_audio(id);
167         ssc->sound_dai = !ret;
168
169         return ret;
170 }
171
172 static void ssc_sound_dai_remove(struct ssc_device *ssc)
173 {
174         if (!ssc->sound_dai)
175                 return;
176
177         atmel_ssc_put_audio(of_alias_get_id(ssc->pdev->dev.of_node, "ssc"));
178 }
179 #else
180 static inline int ssc_sound_dai_probe(struct ssc_device *ssc)
181 {
182         if (of_property_read_bool(ssc->pdev->dev.of_node, "#sound-dai-cells"))
183                 return -ENOTSUPP;
184
185         return 0;
186 }
187
188 static inline void ssc_sound_dai_remove(struct ssc_device *ssc)
189 {
190 }
191 #endif
192
193 static int ssc_probe(struct platform_device *pdev)
194 {
195         struct resource *regs;
196         struct ssc_device *ssc;
197         const struct atmel_ssc_platform_data *plat_dat;
198
199         ssc = devm_kzalloc(&pdev->dev, sizeof(struct ssc_device), GFP_KERNEL);
200         if (!ssc) {
201                 dev_dbg(&pdev->dev, "out of memory\n");
202                 return -ENOMEM;
203         }
204
205         ssc->pdev = pdev;
206
207         plat_dat = atmel_ssc_get_driver_data(pdev);
208         if (!plat_dat)
209                 return -ENODEV;
210         ssc->pdata = (struct atmel_ssc_platform_data *)plat_dat;
211
212         if (pdev->dev.of_node) {
213                 struct device_node *np = pdev->dev.of_node;
214                 ssc->clk_from_rk_pin =
215                         of_property_read_bool(np, "atmel,clk-from-rk-pin");
216         }
217
218         regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
219         ssc->regs = devm_ioremap_resource(&pdev->dev, regs);
220         if (IS_ERR(ssc->regs))
221                 return PTR_ERR(ssc->regs);
222
223         ssc->phybase = regs->start;
224
225         ssc->clk = devm_clk_get(&pdev->dev, "pclk");
226         if (IS_ERR(ssc->clk)) {
227                 dev_dbg(&pdev->dev, "no pclk clock defined\n");
228                 return -ENXIO;
229         }
230
231         /* disable all interrupts */
232         clk_prepare_enable(ssc->clk);
233         ssc_writel(ssc->regs, IDR, -1);
234         ssc_readl(ssc->regs, SR);
235         clk_disable_unprepare(ssc->clk);
236
237         ssc->irq = platform_get_irq(pdev, 0);
238         if (!ssc->irq) {
239                 dev_dbg(&pdev->dev, "could not get irq\n");
240                 return -ENXIO;
241         }
242
243         spin_lock(&user_lock);
244         list_add_tail(&ssc->list, &ssc_list);
245         spin_unlock(&user_lock);
246
247         platform_set_drvdata(pdev, ssc);
248
249         dev_info(&pdev->dev, "Atmel SSC device at 0x%p (irq %d)\n",
250                         ssc->regs, ssc->irq);
251
252         if (ssc_sound_dai_probe(ssc))
253                 dev_err(&pdev->dev, "failed to auto-setup ssc for audio\n");
254
255         return 0;
256 }
257
258 static int ssc_remove(struct platform_device *pdev)
259 {
260         struct ssc_device *ssc = platform_get_drvdata(pdev);
261
262         ssc_sound_dai_remove(ssc);
263
264         spin_lock(&user_lock);
265         list_del(&ssc->list);
266         spin_unlock(&user_lock);
267
268         return 0;
269 }
270
271 static struct platform_driver ssc_driver = {
272         .driver         = {
273                 .name           = "ssc",
274                 .of_match_table = of_match_ptr(atmel_ssc_dt_ids),
275         },
276         .id_table       = atmel_ssc_devtypes,
277         .probe          = ssc_probe,
278         .remove         = ssc_remove,
279 };
280 module_platform_driver(ssc_driver);
281
282 MODULE_AUTHOR("Hans-Christian Egtvedt <hcegtvedt@atmel.com>");
283 MODULE_DESCRIPTION("SSC driver for Atmel AVR32 and AT91");
284 MODULE_LICENSE("GPL");
285 MODULE_ALIAS("platform:ssc");