diff options
Diffstat (limited to 'sound/oss')
-rw-r--r-- | sound/oss/Kconfig | 2 | ||||
-rw-r--r-- | sound/oss/sh_dac_audio.c | 85 |
2 files changed, 33 insertions, 54 deletions
diff --git a/sound/oss/Kconfig b/sound/oss/Kconfig index 1ca7427c4b6d..bcf2a0698d54 100644 --- a/sound/oss/Kconfig +++ b/sound/oss/Kconfig | |||
@@ -561,7 +561,7 @@ endif # SOUND_OSS | |||
561 | 561 | ||
562 | config SOUND_SH_DAC_AUDIO | 562 | config SOUND_SH_DAC_AUDIO |
563 | tristate "SuperH DAC audio support" | 563 | tristate "SuperH DAC audio support" |
564 | depends on CPU_SH3 | 564 | depends on CPU_SH3 && HIGH_RES_TIMERS |
565 | 565 | ||
566 | config SOUND_SH_DAC_AUDIO_CHANNEL | 566 | config SOUND_SH_DAC_AUDIO_CHANNEL |
567 | int "DAC channel" | 567 | int "DAC channel" |
diff --git a/sound/oss/sh_dac_audio.c b/sound/oss/sh_dac_audio.c index 78cfb66e4c59..b2ed8757542a 100644 --- a/sound/oss/sh_dac_audio.c +++ b/sound/oss/sh_dac_audio.c | |||
@@ -18,47 +18,36 @@ | |||
18 | #include <linux/sound.h> | 18 | #include <linux/sound.h> |
19 | #include <linux/soundcard.h> | 19 | #include <linux/soundcard.h> |
20 | #include <linux/interrupt.h> | 20 | #include <linux/interrupt.h> |
21 | #include <linux/hrtimer.h> | ||
21 | #include <asm/io.h> | 22 | #include <asm/io.h> |
22 | #include <asm/uaccess.h> | 23 | #include <asm/uaccess.h> |
23 | #include <asm/irq.h> | 24 | #include <asm/irq.h> |
24 | #include <asm/delay.h> | 25 | #include <asm/delay.h> |
25 | #include <asm/clock.h> | 26 | #include <asm/clock.h> |
26 | #include <asm/cpu/dac.h> | 27 | #include <cpu/dac.h> |
27 | #include <asm/cpu/timer.h> | ||
28 | #include <asm/machvec.h> | 28 | #include <asm/machvec.h> |
29 | #include <mach/hp6xx.h> | 29 | #include <mach/hp6xx.h> |
30 | #include <asm/hd64461.h> | 30 | #include <asm/hd64461.h> |
31 | 31 | ||
32 | #define MODNAME "sh_dac_audio" | 32 | #define MODNAME "sh_dac_audio" |
33 | 33 | ||
34 | #define TMU_TOCR_INIT 0x00 | ||
35 | |||
36 | #define TMU1_TCR_INIT 0x0020 /* Clock/4, rising edge; interrupt on */ | ||
37 | #define TMU1_TSTR_INIT 0x02 /* Bit to turn on TMU1 */ | ||
38 | |||
39 | #define BUFFER_SIZE 48000 | 34 | #define BUFFER_SIZE 48000 |
40 | 35 | ||
41 | static int rate; | 36 | static int rate; |
42 | static int empty; | 37 | static int empty; |
43 | static char *data_buffer, *buffer_begin, *buffer_end; | 38 | static char *data_buffer, *buffer_begin, *buffer_end; |
44 | static int in_use, device_major; | 39 | static int in_use, device_major; |
40 | static struct hrtimer hrtimer; | ||
41 | static ktime_t wakeups_per_second; | ||
45 | 42 | ||
46 | static void dac_audio_start_timer(void) | 43 | static void dac_audio_start_timer(void) |
47 | { | 44 | { |
48 | u8 tstr; | 45 | hrtimer_start(&hrtimer, wakeups_per_second, HRTIMER_MODE_REL); |
49 | |||
50 | tstr = ctrl_inb(TMU_TSTR); | ||
51 | tstr |= TMU1_TSTR_INIT; | ||
52 | ctrl_outb(tstr, TMU_TSTR); | ||
53 | } | 46 | } |
54 | 47 | ||
55 | static void dac_audio_stop_timer(void) | 48 | static void dac_audio_stop_timer(void) |
56 | { | 49 | { |
57 | u8 tstr; | 50 | hrtimer_cancel(&hrtimer); |
58 | |||
59 | tstr = ctrl_inb(TMU_TSTR); | ||
60 | tstr &= ~TMU1_TSTR_INIT; | ||
61 | ctrl_outb(tstr, TMU_TSTR); | ||
62 | } | 51 | } |
63 | 52 | ||
64 | static void dac_audio_reset(void) | 53 | static void dac_audio_reset(void) |
@@ -77,38 +66,30 @@ static void dac_audio_sync(void) | |||
77 | static void dac_audio_start(void) | 66 | static void dac_audio_start(void) |
78 | { | 67 | { |
79 | if (mach_is_hp6xx()) { | 68 | if (mach_is_hp6xx()) { |
80 | u16 v = inw(HD64461_GPADR); | 69 | u16 v = __raw_readw(HD64461_GPADR); |
81 | v &= ~HD64461_GPADR_SPEAKER; | 70 | v &= ~HD64461_GPADR_SPEAKER; |
82 | outw(v, HD64461_GPADR); | 71 | __raw_writew(v, HD64461_GPADR); |
83 | } | 72 | } |
84 | 73 | ||
85 | sh_dac_enable(CONFIG_SOUND_SH_DAC_AUDIO_CHANNEL); | 74 | sh_dac_enable(CONFIG_SOUND_SH_DAC_AUDIO_CHANNEL); |
86 | ctrl_outw(TMU1_TCR_INIT, TMU1_TCR); | ||
87 | } | 75 | } |
88 | static void dac_audio_stop(void) | 76 | static void dac_audio_stop(void) |
89 | { | 77 | { |
90 | dac_audio_stop_timer(); | 78 | dac_audio_stop_timer(); |
91 | 79 | ||
92 | if (mach_is_hp6xx()) { | 80 | if (mach_is_hp6xx()) { |
93 | u16 v = inw(HD64461_GPADR); | 81 | u16 v = __raw_readw(HD64461_GPADR); |
94 | v |= HD64461_GPADR_SPEAKER; | 82 | v |= HD64461_GPADR_SPEAKER; |
95 | outw(v, HD64461_GPADR); | 83 | __raw_writew(v, HD64461_GPADR); |
96 | } | 84 | } |
97 | 85 | ||
98 | sh_dac_output(0, CONFIG_SOUND_SH_DAC_AUDIO_CHANNEL); | 86 | sh_dac_output(0, CONFIG_SOUND_SH_DAC_AUDIO_CHANNEL); |
99 | sh_dac_disable(CONFIG_SOUND_SH_DAC_AUDIO_CHANNEL); | 87 | sh_dac_disable(CONFIG_SOUND_SH_DAC_AUDIO_CHANNEL); |
100 | } | 88 | } |
101 | 89 | ||
102 | static void dac_audio_set_rate(void) | 90 | static void dac_audio_set_rate(void) |
103 | { | 91 | { |
104 | unsigned long interval; | 92 | wakeups_per_second = ktime_set(0, 1000000000 / rate); |
105 | struct clk *clk; | ||
106 | |||
107 | clk = clk_get(NULL, "module_clk"); | ||
108 | interval = (clk_get_rate(clk) / 4) / rate; | ||
109 | clk_put(clk); | ||
110 | ctrl_outl(interval, TMU1_TCOR); | ||
111 | ctrl_outl(interval, TMU1_TCNT); | ||
112 | } | 93 | } |
113 | 94 | ||
114 | static int dac_audio_ioctl(struct inode *inode, struct file *file, | 95 | static int dac_audio_ioctl(struct inode *inode, struct file *file, |
@@ -265,32 +246,26 @@ const struct file_operations dac_audio_fops = { | |||
265 | .release = dac_audio_release, | 246 | .release = dac_audio_release, |
266 | }; | 247 | }; |
267 | 248 | ||
268 | static irqreturn_t timer1_interrupt(int irq, void *dev) | 249 | static enum hrtimer_restart sh_dac_audio_timer(struct hrtimer *handle) |
269 | { | 250 | { |
270 | unsigned long timer_status; | ||
271 | |||
272 | timer_status = ctrl_inw(TMU1_TCR); | ||
273 | timer_status &= ~0x100; | ||
274 | ctrl_outw(timer_status, TMU1_TCR); | ||
275 | |||
276 | if (!empty) { | 251 | if (!empty) { |
277 | sh_dac_output(*buffer_begin, CONFIG_SOUND_SH_DAC_AUDIO_CHANNEL); | 252 | sh_dac_output(*buffer_begin, CONFIG_SOUND_SH_DAC_AUDIO_CHANNEL); |
278 | buffer_begin++; | 253 | buffer_begin++; |
279 | 254 | ||
280 | if (buffer_begin == data_buffer + BUFFER_SIZE) | 255 | if (buffer_begin == data_buffer + BUFFER_SIZE) |
281 | buffer_begin = data_buffer; | 256 | buffer_begin = data_buffer; |
282 | if (buffer_begin == buffer_end) { | 257 | if (buffer_begin == buffer_end) |
283 | empty = 1; | 258 | empty = 1; |
284 | dac_audio_stop_timer(); | ||
285 | } | ||
286 | } | 259 | } |
287 | return IRQ_HANDLED; | 260 | |
261 | if (!empty) | ||
262 | hrtimer_start(&hrtimer, wakeups_per_second, HRTIMER_MODE_REL); | ||
263 | |||
264 | return HRTIMER_NORESTART; | ||
288 | } | 265 | } |
289 | 266 | ||
290 | static int __init dac_audio_init(void) | 267 | static int __init dac_audio_init(void) |
291 | { | 268 | { |
292 | int retval; | ||
293 | |||
294 | if ((device_major = register_sound_dsp(&dac_audio_fops, -1)) < 0) { | 269 | if ((device_major = register_sound_dsp(&dac_audio_fops, -1)) < 0) { |
295 | printk(KERN_ERR "Cannot register dsp device"); | 270 | printk(KERN_ERR "Cannot register dsp device"); |
296 | return device_major; | 271 | return device_major; |
@@ -306,21 +281,25 @@ static int __init dac_audio_init(void) | |||
306 | rate = 8000; | 281 | rate = 8000; |
307 | dac_audio_set_rate(); | 282 | dac_audio_set_rate(); |
308 | 283 | ||
309 | retval = | 284 | /* Today: High Resolution Timer driven DAC playback. |
310 | request_irq(TIMER1_IRQ, timer1_interrupt, IRQF_DISABLED, MODNAME, 0); | 285 | * The timer callback gets called once per sample. Ouch. |
311 | if (retval < 0) { | 286 | * |
312 | printk(KERN_ERR "sh_dac_audio: IRQ %d request failed\n", | 287 | * Future: A much better approach would be to use the |
313 | TIMER1_IRQ); | 288 | * SH7720 CMT+DMAC+DAC hardware combination like this: |
314 | return retval; | 289 | * - Program sample rate using CMT0 or CMT1 |
315 | } | 290 | * - Program DMAC to use CMT for timing and output to DAC |
291 | * - Play sound using DMAC, let CPU sleep. | ||
292 | * - While at it, rewrite this driver to use ALSA. | ||
293 | */ | ||
294 | |||
295 | hrtimer_init(&hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); | ||
296 | hrtimer.function = sh_dac_audio_timer; | ||
316 | 297 | ||
317 | return 0; | 298 | return 0; |
318 | } | 299 | } |
319 | 300 | ||
320 | static void __exit dac_audio_exit(void) | 301 | static void __exit dac_audio_exit(void) |
321 | { | 302 | { |
322 | free_irq(TIMER1_IRQ, 0); | ||
323 | |||
324 | unregister_sound_dsp(device_major); | 303 | unregister_sound_dsp(device_major); |
325 | kfree((void *)data_buffer); | 304 | kfree((void *)data_buffer); |
326 | } | 305 | } |