diff options
Diffstat (limited to 'sound/soc/tegra/tegra30_dam.c')
-rw-r--r-- | sound/soc/tegra/tegra30_dam.c | 650 |
1 files changed, 650 insertions, 0 deletions
diff --git a/sound/soc/tegra/tegra30_dam.c b/sound/soc/tegra/tegra30_dam.c new file mode 100644 index 00000000000..d308179110c --- /dev/null +++ b/sound/soc/tegra/tegra30_dam.c | |||
@@ -0,0 +1,650 @@ | |||
1 | /* | ||
2 | * tegra30_dam.c - Tegra 30 DAM driver | ||
3 | * | ||
4 | * Author: Nikesh Oswal <noswal@nvidia.com> | ||
5 | * Copyright (C) 2011 - NVIDIA, Inc. | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU General Public License | ||
9 | * version 2 as published by the Free Software Foundation. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, but | ||
12 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
14 | * General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
19 | * 02110-1301 USA | ||
20 | * | ||
21 | */ | ||
22 | |||
23 | #include <linux/clk.h> | ||
24 | #include <linux/module.h> | ||
25 | #include <linux/debugfs.h> | ||
26 | #include <linux/device.h> | ||
27 | #include <linux/platform_device.h> | ||
28 | #include <linux/seq_file.h> | ||
29 | #include <linux/slab.h> | ||
30 | #include <linux/io.h> | ||
31 | #include <sound/soc.h> | ||
32 | #include "tegra30_dam.h" | ||
33 | #include "tegra30_ahub.h" | ||
34 | |||
35 | #define DRV_NAME "tegra30-dam" | ||
36 | |||
37 | static struct tegra30_dam_context *dams_cont_info[TEGRA30_NR_DAM_IFC]; | ||
38 | |||
39 | enum { | ||
40 | dam_ch_in0 = 0x0, | ||
41 | dam_ch_in1, | ||
42 | dam_ch_out, | ||
43 | dam_ch_maxnum | ||
44 | } tegra30_dam_chtype; | ||
45 | |||
46 | struct tegra30_dam_src_step_table step_table[] = { | ||
47 | { 8000, 44100, 80 }, | ||
48 | { 8000, 48000, 1 }, | ||
49 | { 16000, 44100, 160 }, | ||
50 | { 16000, 48000, 1 }, | ||
51 | { 44100, 8000, 441 }, | ||
52 | { 48000, 8000, 0 }, | ||
53 | { 44100, 16000, 441 }, | ||
54 | { 48000, 16000, 0 }, | ||
55 | }; | ||
56 | |||
57 | static void tegra30_dam_set_output_samplerate(struct tegra30_dam_context *dam, | ||
58 | int fsout); | ||
59 | static void tegra30_dam_set_input_samplerate(struct tegra30_dam_context *dam, | ||
60 | int fsin); | ||
61 | static int tegra30_dam_set_step_reset(struct tegra30_dam_context *dam, | ||
62 | int insample, int outsample); | ||
63 | static void tegra30_dam_ch0_set_step(struct tegra30_dam_context *dam, int step); | ||
64 | |||
65 | static inline void tegra30_dam_writel(struct tegra30_dam_context *dam, | ||
66 | u32 val, u32 reg) | ||
67 | { | ||
68 | #ifdef CONFIG_PM | ||
69 | dam->reg_cache[reg >> 2] = val; | ||
70 | #endif | ||
71 | __raw_writel(val, dam->damregs + reg); | ||
72 | } | ||
73 | |||
74 | static inline u32 tegra30_dam_readl(struct tegra30_dam_context *dam, u32 reg) | ||
75 | { | ||
76 | u32 val = __raw_readl(dam->damregs + reg); | ||
77 | |||
78 | return val; | ||
79 | } | ||
80 | |||
81 | #ifdef CONFIG_PM | ||
82 | int tegra30_dam_resume(int ifc) | ||
83 | { | ||
84 | int i = 0; | ||
85 | struct tegra30_dam_context *dam; | ||
86 | |||
87 | if (ifc >= TEGRA30_NR_DAM_IFC) | ||
88 | return -EINVAL; | ||
89 | |||
90 | dam = dams_cont_info[ifc]; | ||
91 | |||
92 | if (dam->in_use) { | ||
93 | tegra30_dam_enable_clock(ifc); | ||
94 | |||
95 | for (i = 0; i <= TEGRA30_DAM_CTRL_REGINDEX; i++) { | ||
96 | if ((i == TEGRA30_DAM_CTRL_RSVD_6) || | ||
97 | (i == TEGRA30_DAM_CTRL_RSVD_10)) | ||
98 | continue; | ||
99 | |||
100 | tegra30_dam_writel(dam, dam->reg_cache[i], | ||
101 | (i << 2)); | ||
102 | } | ||
103 | |||
104 | tegra30_dam_disable_clock(ifc); | ||
105 | } | ||
106 | |||
107 | return 0; | ||
108 | } | ||
109 | #endif | ||
110 | |||
111 | void tegra30_dam_disable_clock(int ifc) | ||
112 | { | ||
113 | struct tegra30_dam_context *dam; | ||
114 | |||
115 | if (ifc >= TEGRA30_NR_DAM_IFC) | ||
116 | return; | ||
117 | |||
118 | dam = dams_cont_info[ifc]; | ||
119 | clk_disable(dam->dam_clk); | ||
120 | tegra30_ahub_disable_clocks(); | ||
121 | } | ||
122 | |||
123 | int tegra30_dam_enable_clock(int ifc) | ||
124 | { | ||
125 | struct tegra30_dam_context *dam; | ||
126 | |||
127 | if (ifc >= TEGRA30_NR_DAM_IFC) | ||
128 | return -EINVAL; | ||
129 | |||
130 | dam = dams_cont_info[ifc]; | ||
131 | tegra30_ahub_enable_clocks(); | ||
132 | clk_enable(dam->dam_clk); | ||
133 | |||
134 | return 0; | ||
135 | } | ||
136 | |||
137 | #ifdef CONFIG_DEBUG_FS | ||
138 | static int tegra30_dam_show(struct seq_file *s, void *unused) | ||
139 | { | ||
140 | #define REG(r) { r, #r } | ||
141 | static const struct { | ||
142 | int offset; | ||
143 | const char *name; | ||
144 | } regs[] = { | ||
145 | REG(TEGRA30_DAM_CTRL), | ||
146 | REG(TEGRA30_DAM_CLIP), | ||
147 | REG(TEGRA30_DAM_CLIP_THRESHOLD), | ||
148 | REG(TEGRA30_DAM_AUDIOCIF_OUT_CTRL), | ||
149 | REG(TEGRA30_DAM_CH0_CTRL), | ||
150 | REG(TEGRA30_DAM_CH0_CONV), | ||
151 | REG(TEGRA30_DAM_AUDIOCIF_CH0_CTRL), | ||
152 | REG(TEGRA30_DAM_CH1_CTRL), | ||
153 | REG(TEGRA30_DAM_CH1_CONV), | ||
154 | REG(TEGRA30_DAM_AUDIOCIF_CH1_CTRL), | ||
155 | }; | ||
156 | #undef REG | ||
157 | |||
158 | struct tegra30_dam_context *dam = s->private; | ||
159 | int i; | ||
160 | |||
161 | clk_enable(dam->dam_clk); | ||
162 | |||
163 | for (i = 0; i < ARRAY_SIZE(regs); i++) { | ||
164 | u32 val = tegra30_dam_readl(dam, regs[i].offset); | ||
165 | seq_printf(s, "%s = %08x\n", regs[i].name, val); | ||
166 | } | ||
167 | |||
168 | clk_disable(dam->dam_clk); | ||
169 | |||
170 | return 0; | ||
171 | } | ||
172 | |||
173 | static int tegra30_dam_debug_open(struct inode *inode, struct file *file) | ||
174 | { | ||
175 | return single_open(file, tegra30_dam_show, inode->i_private); | ||
176 | } | ||
177 | |||
178 | static const struct file_operations tegra30_dam_debug_fops = { | ||
179 | .open = tegra30_dam_debug_open, | ||
180 | .read = seq_read, | ||
181 | .llseek = seq_lseek, | ||
182 | .release = single_release, | ||
183 | }; | ||
184 | |||
185 | static void tegra30_dam_debug_add(struct tegra30_dam_context *dam, int id) | ||
186 | { | ||
187 | char name[] = DRV_NAME ".0"; | ||
188 | |||
189 | snprintf(name, sizeof(name), DRV_NAME".%1d", id); | ||
190 | dam->debug = debugfs_create_file(name, S_IRUGO, snd_soc_debugfs_root, | ||
191 | dam, &tegra30_dam_debug_fops); | ||
192 | } | ||
193 | |||
194 | static void tegra30_dam_debug_remove(struct tegra30_dam_context *dam) | ||
195 | { | ||
196 | if (dam->debug) | ||
197 | debugfs_remove(dam->debug); | ||
198 | } | ||
199 | #else | ||
200 | static inline void tegra30_dam_debug_add(struct tegra30_dam_context *dam, | ||
201 | int id) | ||
202 | { | ||
203 | } | ||
204 | |||
205 | static inline void tegra30_dam_debug_remove(struct tegra30_dam_context *dam) | ||
206 | { | ||
207 | } | ||
208 | #endif | ||
209 | |||
210 | int tegra30_dam_allocate_controller() | ||
211 | { | ||
212 | int i = 0; | ||
213 | struct tegra30_dam_context *dam = NULL; | ||
214 | |||
215 | for (i = 0; i < TEGRA30_NR_DAM_IFC; i++) { | ||
216 | |||
217 | dam = dams_cont_info[i]; | ||
218 | |||
219 | if (!dam->in_use) { | ||
220 | dam->in_use = true; | ||
221 | return i; | ||
222 | } | ||
223 | } | ||
224 | |||
225 | return -ENOENT; | ||
226 | } | ||
227 | |||
228 | int tegra30_dam_allocate_channel(int ifc, int chid) | ||
229 | { | ||
230 | struct tegra30_dam_context *dam = NULL; | ||
231 | |||
232 | if (ifc >= TEGRA30_NR_DAM_IFC) | ||
233 | return -EINVAL; | ||
234 | |||
235 | dam = dams_cont_info[ifc]; | ||
236 | |||
237 | if (!dam->ch_alloc[chid]) { | ||
238 | dam->ch_alloc[chid] = true; | ||
239 | return 0; | ||
240 | } | ||
241 | |||
242 | return -ENOENT; | ||
243 | } | ||
244 | |||
245 | int tegra30_dam_free_channel(int ifc, int chid) | ||
246 | { | ||
247 | struct tegra30_dam_context *dam = NULL; | ||
248 | |||
249 | if (ifc >= TEGRA30_NR_DAM_IFC) | ||
250 | return -EINVAL; | ||
251 | |||
252 | dam = dams_cont_info[ifc]; | ||
253 | |||
254 | if (dam->ch_alloc[chid]) { | ||
255 | dam->ch_alloc[chid] = false; | ||
256 | return 0; | ||
257 | } | ||
258 | |||
259 | return -EINVAL; | ||
260 | } | ||
261 | |||
262 | int tegra30_dam_free_controller(int ifc) | ||
263 | { | ||
264 | struct tegra30_dam_context *dam = NULL; | ||
265 | |||
266 | if (ifc >= TEGRA30_NR_DAM_IFC) | ||
267 | return -EINVAL; | ||
268 | |||
269 | dam = dams_cont_info[ifc]; | ||
270 | |||
271 | if (!dam->ch_alloc[dam_ch_in0] && | ||
272 | !dam->ch_alloc[dam_ch_in1]) { | ||
273 | dam->in_use = false; | ||
274 | return 0; | ||
275 | } | ||
276 | |||
277 | return -EINVAL; | ||
278 | } | ||
279 | |||
280 | void tegra30_dam_set_samplerate(int ifc, int chid, int samplerate) | ||
281 | { | ||
282 | struct tegra30_dam_context *dam = dams_cont_info[ifc]; | ||
283 | |||
284 | if (ifc >= TEGRA30_NR_DAM_IFC) | ||
285 | return; | ||
286 | |||
287 | switch (chid) { | ||
288 | case dam_ch_in0: | ||
289 | tegra30_dam_set_input_samplerate(dam, samplerate); | ||
290 | dam->ch_insamplerate[dam_ch_in0] = samplerate; | ||
291 | tegra30_dam_set_step_reset(dam, samplerate, dam->outsamplerate); | ||
292 | break; | ||
293 | case dam_ch_in1: | ||
294 | if (samplerate != dam->outsamplerate) | ||
295 | return; | ||
296 | dam->ch_insamplerate[dam_ch_in1] = samplerate; | ||
297 | break; | ||
298 | case dam_ch_out: | ||
299 | tegra30_dam_set_output_samplerate(dam, samplerate); | ||
300 | dam->outsamplerate = samplerate; | ||
301 | break; | ||
302 | default: | ||
303 | break; | ||
304 | } | ||
305 | } | ||
306 | |||
307 | void tegra30_dam_set_output_samplerate(struct tegra30_dam_context *dam, | ||
308 | int fsout) | ||
309 | { | ||
310 | u32 val; | ||
311 | |||
312 | val = tegra30_dam_readl(dam, TEGRA30_DAM_CTRL); | ||
313 | val &= ~TEGRA30_DAM_CTRL_FSOUT_MASK; | ||
314 | |||
315 | switch (fsout) { | ||
316 | case TEGRA30_AUDIO_SAMPLERATE_8000: | ||
317 | val |= TEGRA30_DAM_CTRL_FSOUT_FS8; | ||
318 | break; | ||
319 | case TEGRA30_AUDIO_SAMPLERATE_16000: | ||
320 | val |= TEGRA30_DAM_CTRL_FSOUT_FS16; | ||
321 | break; | ||
322 | case TEGRA30_AUDIO_SAMPLERATE_44100: | ||
323 | val |= TEGRA30_DAM_CTRL_FSOUT_FS44; | ||
324 | break; | ||
325 | case TEGRA30_AUDIO_SAMPLERATE_48000: | ||
326 | val |= TEGRA30_DAM_CTRL_FSOUT_FS48; | ||
327 | break; | ||
328 | default: | ||
329 | break; | ||
330 | } | ||
331 | |||
332 | tegra30_dam_writel(dam, val, TEGRA30_DAM_CTRL); | ||
333 | } | ||
334 | |||
335 | void tegra30_dam_set_input_samplerate(struct tegra30_dam_context *dam, int fsin) | ||
336 | { | ||
337 | u32 val; | ||
338 | |||
339 | val = tegra30_dam_readl(dam, TEGRA30_DAM_CH0_CTRL); | ||
340 | val &= ~TEGRA30_DAM_CH0_CTRL_FSIN_MASK; | ||
341 | |||
342 | switch (fsin) { | ||
343 | case TEGRA30_AUDIO_SAMPLERATE_8000: | ||
344 | val |= TEGRA30_DAM_CH0_CTRL_FSIN_FS8; | ||
345 | break; | ||
346 | case TEGRA30_AUDIO_SAMPLERATE_16000: | ||
347 | val |= TEGRA30_DAM_CH0_CTRL_FSIN_FS16; | ||
348 | break; | ||
349 | case TEGRA30_AUDIO_SAMPLERATE_44100: | ||
350 | val |= TEGRA30_DAM_CH0_CTRL_FSIN_FS44; | ||
351 | break; | ||
352 | case TEGRA30_AUDIO_SAMPLERATE_48000: | ||
353 | val |= TEGRA30_DAM_CH0_CTRL_FSIN_FS48; | ||
354 | break; | ||
355 | default: | ||
356 | break; | ||
357 | } | ||
358 | |||
359 | tegra30_dam_writel(dam, val, TEGRA30_DAM_CH0_CTRL); | ||
360 | } | ||
361 | |||
362 | int tegra30_dam_set_step_reset(struct tegra30_dam_context *dam, | ||
363 | int insample, int outsample) | ||
364 | { | ||
365 | int step_reset = 0; | ||
366 | int i = 0; | ||
367 | |||
368 | for (i = 0; i < ARRAY_SIZE(step_table); i++) { | ||
369 | if ((insample == step_table[i].insample) && | ||
370 | (outsample == step_table[i].outsample)) | ||
371 | step_reset = step_table[i].stepreset; | ||
372 | } | ||
373 | |||
374 | tegra30_dam_ch0_set_step(dam, step_reset); | ||
375 | |||
376 | return 0; | ||
377 | } | ||
378 | |||
379 | void tegra30_dam_ch0_set_step(struct tegra30_dam_context *dam, int step) | ||
380 | { | ||
381 | u32 val; | ||
382 | |||
383 | val = tegra30_dam_readl(dam, TEGRA30_DAM_CH0_CTRL); | ||
384 | val &= ~TEGRA30_DAM_CH0_CTRL_STEP_MASK; | ||
385 | val |= step << TEGRA30_DAM_CH0_CTRL_STEP_SHIFT; | ||
386 | tegra30_dam_writel(dam, val, TEGRA30_DAM_CH0_CTRL); | ||
387 | } | ||
388 | |||
389 | int tegra30_dam_set_gain(int ifc, int chid, int gain) | ||
390 | { | ||
391 | |||
392 | if (ifc >= TEGRA30_NR_DAM_IFC) | ||
393 | return -EINVAL; | ||
394 | |||
395 | switch (chid) { | ||
396 | case dam_ch_in0: | ||
397 | tegra30_dam_writel(dams_cont_info[ifc], gain, | ||
398 | TEGRA30_DAM_CH0_CONV); | ||
399 | break; | ||
400 | case dam_ch_in1: | ||
401 | tegra30_dam_writel(dams_cont_info[ifc], gain, | ||
402 | TEGRA30_DAM_CH1_CONV); | ||
403 | break; | ||
404 | default: | ||
405 | break; | ||
406 | } | ||
407 | |||
408 | return 0; | ||
409 | } | ||
410 | |||
411 | int tegra30_dam_set_acif(int ifc, int chid, unsigned int audio_channels, | ||
412 | unsigned int audio_bits, unsigned int client_channels, | ||
413 | unsigned int client_bits) | ||
414 | { | ||
415 | unsigned int reg; | ||
416 | unsigned int value = 0; | ||
417 | |||
418 | if (ifc >= TEGRA30_NR_DAM_IFC) | ||
419 | return -EINVAL; | ||
420 | |||
421 | /*ch0 takes input as mono/16bit always*/ | ||
422 | if ((chid == dam_ch_in0) && | ||
423 | ((client_channels != 1) || (client_bits != 16))) | ||
424 | return -EINVAL; | ||
425 | |||
426 | value |= TEGRA30_CIF_MONOCONV_COPY; | ||
427 | value |= TEGRA30_CIF_STEREOCONV_CH0; | ||
428 | value |= (audio_channels-1) << TEGRA30_AUDIO_CHANNELS_SHIFT; | ||
429 | value |= (((audio_bits>>2)-1)<<TEGRA30_AUDIO_BITS_SHIFT); | ||
430 | value |= (client_channels-1) << TEGRA30_CLIENT_CHANNELS_SHIFT; | ||
431 | value |= (((client_bits>>2)-1)<<TEGRA30_CLIENT_BITS_SHIFT); | ||
432 | |||
433 | switch (chid) { | ||
434 | case dam_ch_out: | ||
435 | value |= TEGRA30_CIF_DIRECTION_TX; | ||
436 | reg = TEGRA30_DAM_AUDIOCIF_OUT_CTRL; | ||
437 | break; | ||
438 | case dam_ch_in0: | ||
439 | value |= TEGRA30_CIF_DIRECTION_RX; | ||
440 | reg = TEGRA30_DAM_AUDIOCIF_CH0_CTRL; | ||
441 | break; | ||
442 | case dam_ch_in1: | ||
443 | value |= TEGRA30_CIF_DIRECTION_RX; | ||
444 | reg = TEGRA30_DAM_AUDIOCIF_CH1_CTRL; | ||
445 | break; | ||
446 | default: | ||
447 | return -EINVAL; | ||
448 | } | ||
449 | |||
450 | tegra30_dam_writel(dams_cont_info[ifc], value, reg); | ||
451 | |||
452 | return 0; | ||
453 | } | ||
454 | |||
455 | void tegra30_dam_enable(int ifc, int on, int chid) | ||
456 | { | ||
457 | u32 old_val, val, enreg; | ||
458 | struct tegra30_dam_context *dam = dams_cont_info[ifc]; | ||
459 | |||
460 | if (ifc >= TEGRA30_NR_DAM_IFC) | ||
461 | return; | ||
462 | |||
463 | if (chid == dam_ch_in0) | ||
464 | enreg = TEGRA30_DAM_CH0_CTRL; | ||
465 | else | ||
466 | enreg = TEGRA30_DAM_CH1_CTRL; | ||
467 | |||
468 | old_val = val = tegra30_dam_readl(dam, enreg); | ||
469 | |||
470 | if (on) { | ||
471 | if (!dam->ch_enable_refcnt[chid]++) | ||
472 | val |= TEGRA30_DAM_CH0_CTRL_EN; | ||
473 | } else if (dam->ch_enable_refcnt[chid]) { | ||
474 | dam->ch_enable_refcnt[chid]--; | ||
475 | if (!dam->ch_enable_refcnt[chid]) | ||
476 | val &= ~TEGRA30_DAM_CH0_CTRL_EN; | ||
477 | } | ||
478 | |||
479 | if (val != old_val) | ||
480 | tegra30_dam_writel(dam, val, enreg); | ||
481 | |||
482 | old_val = val = tegra30_dam_readl(dam, TEGRA30_DAM_CTRL); | ||
483 | |||
484 | if (dam->ch_enable_refcnt[dam_ch_in0] || | ||
485 | dam->ch_enable_refcnt[dam_ch_in1]) | ||
486 | val |= TEGRA30_DAM_CTRL_DAM_EN; | ||
487 | else | ||
488 | val &= ~TEGRA30_DAM_CTRL_DAM_EN; | ||
489 | |||
490 | if (old_val != val) | ||
491 | tegra30_dam_writel(dam, val, TEGRA30_DAM_CTRL); | ||
492 | } | ||
493 | |||
494 | void tegra30_dam_ch0_set_datasync(struct tegra30_dam_context *dam, int datasync) | ||
495 | { | ||
496 | u32 val; | ||
497 | |||
498 | val = tegra30_dam_readl(dam, TEGRA30_DAM_CH0_CTRL); | ||
499 | val &= ~TEGRA30_DAM_CH0_CTRL_DATA_SYNC_MASK; | ||
500 | val |= datasync << TEGRA30_DAM_DATA_SYNC_SHIFT; | ||
501 | tegra30_dam_writel(dam, val, TEGRA30_DAM_CH0_CTRL); | ||
502 | } | ||
503 | |||
504 | void tegra30_dam_ch1_set_datasync(struct tegra30_dam_context *dam, int datasync) | ||
505 | { | ||
506 | u32 val; | ||
507 | |||
508 | val = tegra30_dam_readl(dam, TEGRA30_DAM_CH1_CTRL); | ||
509 | val &= ~TEGRA30_DAM_CH1_CTRL_DATA_SYNC_MASK; | ||
510 | val |= datasync << TEGRA30_DAM_DATA_SYNC_SHIFT; | ||
511 | tegra30_dam_writel(dam, val, TEGRA30_DAM_CH1_CTRL); | ||
512 | } | ||
513 | |||
514 | void tegra30_dam_enable_clip_counter(struct tegra30_dam_context *dam, int on) | ||
515 | { | ||
516 | u32 val; | ||
517 | |||
518 | val = tegra30_dam_readl(dam, TEGRA30_DAM_CLIP); | ||
519 | val &= ~TEGRA30_DAM_CLIP_COUNTER_ENABLE; | ||
520 | val |= on ? TEGRA30_DAM_CLIP_COUNTER_ENABLE : 0; | ||
521 | tegra30_dam_writel(dam, val, TEGRA30_DAM_CLIP); | ||
522 | } | ||
523 | |||
524 | static int __devinit tegra30_dam_probe(struct platform_device *pdev) | ||
525 | { | ||
526 | struct resource *res, *region; | ||
527 | struct tegra30_dam_context *dam; | ||
528 | int ret = 0; | ||
529 | #ifdef CONFIG_PM | ||
530 | int i; | ||
531 | #endif | ||
532 | int clkm_rate; | ||
533 | |||
534 | if ((pdev->id < 0) || | ||
535 | (pdev->id >= TEGRA30_NR_DAM_IFC)) { | ||
536 | dev_err(&pdev->dev, "ID %d out of range\n", pdev->id); | ||
537 | return -EINVAL; | ||
538 | } | ||
539 | |||
540 | dams_cont_info[pdev->id] = devm_kzalloc(&pdev->dev, | ||
541 | sizeof(struct tegra30_dam_context), | ||
542 | GFP_KERNEL); | ||
543 | if (!dams_cont_info[pdev->id]) { | ||
544 | dev_err(&pdev->dev, "Can't allocate dam context\n"); | ||
545 | ret = -ENOMEM; | ||
546 | goto exit; | ||
547 | } | ||
548 | dam = dams_cont_info[pdev->id]; | ||
549 | |||
550 | dam->dam_clk = clk_get(&pdev->dev, NULL); | ||
551 | if (IS_ERR(dam->dam_clk)) { | ||
552 | dev_err(&pdev->dev, "Can't retrieve dam clock\n"); | ||
553 | ret = PTR_ERR(dam->dam_clk); | ||
554 | goto err_free; | ||
555 | } | ||
556 | clkm_rate = clk_get_rate(clk_get_parent(dam->dam_clk)); | ||
557 | while (clkm_rate > 12000000) | ||
558 | clkm_rate >>= 1; | ||
559 | |||
560 | clk_set_rate(dam->dam_clk,clkm_rate); | ||
561 | |||
562 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
563 | if (!res) { | ||
564 | dev_err(&pdev->dev, "No memory 0 resource\n"); | ||
565 | ret = -ENODEV; | ||
566 | goto err_clk_put_dam; | ||
567 | } | ||
568 | |||
569 | region = devm_request_mem_region(&pdev->dev, res->start, | ||
570 | resource_size(res), pdev->name); | ||
571 | if (!region) { | ||
572 | dev_err(&pdev->dev, "Memory region 0 already claimed\n"); | ||
573 | ret = -EBUSY; | ||
574 | goto err_clk_put_dam; | ||
575 | } | ||
576 | |||
577 | dam->damregs = devm_ioremap(&pdev->dev, res->start, resource_size(res)); | ||
578 | if (!dam->damregs) { | ||
579 | dev_err(&pdev->dev, "ioremap 0 failed\n"); | ||
580 | ret = -ENOMEM; | ||
581 | goto err_clk_put_dam; | ||
582 | } | ||
583 | |||
584 | #ifdef CONFIG_PM | ||
585 | /* cache the POR values of DAM regs*/ | ||
586 | tegra30_dam_enable_clock(pdev->id); | ||
587 | |||
588 | for (i = 0; i <= TEGRA30_DAM_CTRL_REGINDEX; i++) { | ||
589 | if ((i == TEGRA30_DAM_CTRL_RSVD_6) || | ||
590 | (i == TEGRA30_DAM_CTRL_RSVD_10)) | ||
591 | continue; | ||
592 | |||
593 | dam->reg_cache[i] = | ||
594 | tegra30_dam_readl(dam, i << 2); | ||
595 | } | ||
596 | |||
597 | tegra30_dam_disable_clock(pdev->id); | ||
598 | #endif | ||
599 | |||
600 | platform_set_drvdata(pdev, dam); | ||
601 | |||
602 | tegra30_dam_debug_add(dam, pdev->id); | ||
603 | |||
604 | return 0; | ||
605 | |||
606 | err_clk_put_dam: | ||
607 | clk_put(dam->dam_clk); | ||
608 | err_free: | ||
609 | dams_cont_info[pdev->id] = NULL; | ||
610 | exit: | ||
611 | return ret; | ||
612 | } | ||
613 | |||
614 | static int __devexit tegra30_dam_remove(struct platform_device *pdev) | ||
615 | { | ||
616 | struct tegra30_dam_context *dam; | ||
617 | |||
618 | dam = platform_get_drvdata(pdev); | ||
619 | clk_put(dam->dam_clk); | ||
620 | tegra30_dam_debug_remove(dam); | ||
621 | dams_cont_info[pdev->id] = NULL; | ||
622 | |||
623 | return 0; | ||
624 | } | ||
625 | |||
626 | static struct platform_driver tegra30_dam_driver = { | ||
627 | .probe = tegra30_dam_probe, | ||
628 | .remove = __devexit_p(tegra30_dam_remove), | ||
629 | .driver = { | ||
630 | .name = DRV_NAME, | ||
631 | .owner = THIS_MODULE, | ||
632 | }, | ||
633 | }; | ||
634 | |||
635 | static int __init tegra30_dam_modinit(void) | ||
636 | { | ||
637 | return platform_driver_register(&tegra30_dam_driver); | ||
638 | } | ||
639 | module_init(tegra30_dam_modinit); | ||
640 | |||
641 | static void __exit tegra30_dam_modexit(void) | ||
642 | { | ||
643 | platform_driver_unregister(&tegra30_dam_driver); | ||
644 | } | ||
645 | module_exit(tegra30_dam_modexit); | ||
646 | |||
647 | MODULE_AUTHOR("Nikesh Oswal <noswal@nvidia.com>"); | ||
648 | MODULE_DESCRIPTION("Tegra 30 DAM driver"); | ||
649 | MODULE_LICENSE("GPL"); | ||
650 | MODULE_ALIAS("platform:" DRV_NAME); | ||