source: rtems/c/src/lib/libbsp/lm32/shared/milkymist_ac97/ac97.c @ dce1032b

4.115
Last change on this file since dce1032b was dce1032b, checked in by Joel Sherrill <joel.sherrill@…>, on 08/01/11 at 13:48:40

2011-08-01 Sebastien Bourdeauducq <sebastien.bourdeauducq@…>

PR 1869/bsps

  • startup/bspclean.c: New file.
  • include/tm27.h: Removed.
  • ChangeLog?, Makefile.am, README, preinstall.am, include/bsp.h, include/system_conf.h, make/custom/milkymist.cfg, startup/linkcmds: Complete BSP for Milkymist One supporting Milkymist SOC 1.0.x. Includes new or updated drivers for:
    • Multi-standard video input (PAL/SECAM/NTSC)
    • Two DMX512 (RS485) ports
    • MIDI IN and MIDI OUT ports
    • VGA output
    • AC'97 audio
    • NOR flash
    • 10/100 Ethernet
    • Memory card (experimental and incomplete)
    • USB host connectors (input devices only)
    • RC5 infrared receiver
    • RS232 debug port
  • Property mode set to 100644
File size: 10.1 KB
Line 
1/*  ac97.c
2 *
3 *  Sound driver for Milkymist SoC
4 *
5 *  The license and distribution terms for this file may be
6 *  found in the file LICENSE in this distribution or at
7 *  http://www.rtems.com/license/LICENSE.
8 *
9 *  $Id$
10 *
11 *  COPYRIGHT (c) 2010, 2011 Sebastien Bourdeauducq
12 */
13
14#define RTEMS_STATUS_CHECKS_USE_PRINTK
15
16#include <rtems.h>
17#include <bsp.h>
18#include <bsp/irq-generic.h>
19#include <rtems/libio.h>
20#include <rtems/status-checks.h>
21#include "../include/system_conf.h"
22#include "milkymist_ac97.h"
23
24#define SND_DEVICE_NAME "/dev/snd"
25#define MIXER_DEVICE_NAME "/dev/mixer"
26
27static rtems_id cr_write_sem;
28static rtems_id cr_read_sem;
29
30static rtems_isr crrequest_handler(rtems_vector_number n)
31{
32  rtems_semaphore_release(cr_write_sem);
33  lm32_interrupt_ack(1 << MM_IRQ_AC97CRREQUEST);
34}
35
36static rtems_isr crreply_handler(rtems_vector_number n)
37{
38  rtems_semaphore_release(cr_read_sem);
39  lm32_interrupt_ack(1 << MM_IRQ_AC97CRREPLY);
40}
41
42/* queued playback buffers */
43#define PLAY_Q_SIZE 8
44#define PLAY_Q_MASK (PLAY_Q_SIZE-1)
45
46static struct snd_buffer *play_q[PLAY_Q_SIZE];
47static int play_produce;
48static int play_consume;
49static int play_level;
50
51/* buffers played, for application to collect */
52static rtems_id play_q_done;
53
54static void play_start(struct snd_buffer *buf)
55{
56  if (buf->nsamples > (AC97_MAX_DMASIZE/4))
57    buf->nsamples = AC97_MAX_DMASIZE/4;
58
59  MM_WRITE(MM_AC97_DADDRESS, (unsigned int)buf->samples);
60  MM_WRITE(MM_AC97_DREMAINING, buf->nsamples*4);
61  MM_WRITE(MM_AC97_DCTL, AC97_SCTL_EN);
62}
63
64static rtems_isr pcmplay_handler(rtems_vector_number n)
65{
66  lm32_interrupt_ack(1 << MM_IRQ_AC97DMAR);
67
68  rtems_message_queue_send(play_q_done, &play_q[play_consume],
69    sizeof(void *));
70
71  play_consume = (play_consume + 1) & PLAY_Q_MASK;
72  play_level--;
73
74  if(play_level > 0)
75    play_start(play_q[play_consume]);
76  else
77    MM_WRITE(MM_AC97_DCTL, 0);
78}
79
80/* queued record buffers */
81#define RECORD_Q_SIZE 8
82#define RECORD_Q_MASK (RECORD_Q_SIZE-1)
83
84static struct snd_buffer *record_q[RECORD_Q_SIZE];
85static int record_produce;
86static int record_consume;
87static int record_level;
88
89/* buffers recorded, for application to collect */
90static rtems_id record_q_done;
91
92static void record_start(struct snd_buffer *buf)
93{
94  if (buf->nsamples > (AC97_MAX_DMASIZE/4))
95    buf->nsamples = AC97_MAX_DMASIZE/4;
96
97  MM_WRITE(MM_AC97_UADDRESS, (unsigned int)buf->samples);
98  MM_WRITE(MM_AC97_UREMAINING, buf->nsamples*4);
99  MM_WRITE(MM_AC97_UCTL, AC97_SCTL_EN);
100}
101
102static rtems_isr pcmrecord_handler(rtems_vector_number n)
103{
104  lm32_interrupt_ack(1 << MM_IRQ_AC97DMAW);
105
106  __asm__ volatile( /* Invalidate Level-1 data cache */
107      "wcsr DCC, r0\n"
108      "nop\n"
109    );
110
111  rtems_message_queue_send(record_q_done, &record_q[record_consume],
112    sizeof(void *));
113
114  record_consume = (record_consume + 1) & RECORD_Q_MASK;
115  record_level--;
116
117  if(record_level > 0)
118    record_start(record_q[record_consume]);
119  else
120    MM_WRITE(MM_AC97_UCTL, 0);
121}
122
123rtems_device_driver ac97_initialize(
124  rtems_device_major_number major,
125  rtems_device_minor_number minor,
126  void *arg
127)
128{
129  rtems_status_code sc;
130  rtems_isr_entry dummy;
131
132  sc = rtems_io_register_name(SND_DEVICE_NAME, major, 0);
133  RTEMS_CHECK_SC(sc, "create snd device");
134
135  sc = rtems_io_register_name(MIXER_DEVICE_NAME, major, 1);
136  RTEMS_CHECK_SC(sc, "create mixer device");
137
138  sc = rtems_semaphore_create(
139    rtems_build_name('C', 'R', 'W', 'S'),
140    0,
141    RTEMS_SIMPLE_BINARY_SEMAPHORE,
142    0,
143    &cr_write_sem
144  );
145  RTEMS_CHECK_SC(sc, "create AC97 register write semaphore");
146
147  sc = rtems_semaphore_create(
148    rtems_build_name('C', 'R', 'R', 'S'),
149    0,
150    RTEMS_SIMPLE_BINARY_SEMAPHORE,
151    0,
152    &cr_read_sem
153  );
154  RTEMS_CHECK_SC(sc, "create AC97 register read semaphore");
155
156  sc = rtems_message_queue_create(
157    rtems_build_name('P', 'L', 'Y', 'Q'),
158    PLAY_Q_SIZE*2,
159    sizeof(void *),
160    0,
161    &play_q_done
162  );
163  RTEMS_CHECK_SC(sc, "create playback done queue");
164
165  sc = rtems_message_queue_create(
166    rtems_build_name('R', 'E', 'C', 'Q'),
167    RECORD_Q_SIZE*2,
168    sizeof(void *),
169    0,
170    &record_q_done
171  );
172  RTEMS_CHECK_SC(sc, "create record done queue");
173
174  rtems_interrupt_catch(crrequest_handler, MM_IRQ_AC97CRREQUEST, &dummy);
175  rtems_interrupt_catch(crreply_handler, MM_IRQ_AC97CRREPLY, &dummy);
176  rtems_interrupt_catch(pcmplay_handler, MM_IRQ_AC97DMAR, &dummy);
177  rtems_interrupt_catch(pcmrecord_handler, MM_IRQ_AC97DMAW, &dummy);
178  bsp_interrupt_vector_enable(MM_IRQ_AC97CRREQUEST);
179  bsp_interrupt_vector_enable(MM_IRQ_AC97CRREPLY);
180  bsp_interrupt_vector_enable(MM_IRQ_AC97DMAR);
181  bsp_interrupt_vector_enable(MM_IRQ_AC97DMAW);
182
183  play_produce = 0;
184  play_consume = 0;
185  play_level = 0;
186
187  record_produce = 0;
188  record_consume = 0;
189  record_level = 0;
190
191  return RTEMS_SUCCESSFUL;
192}
193
194static rtems_status_code submit_play(struct snd_buffer *buf)
195{
196  bsp_interrupt_vector_disable(MM_IRQ_AC97DMAR);
197  if (play_level == PLAY_Q_SIZE) {
198    bsp_interrupt_vector_enable(MM_IRQ_AC97DMAR);
199    return RTEMS_UNSATISFIED;
200  }
201  play_q[play_produce] = buf;
202  play_produce = (play_produce + 1) & PLAY_Q_MASK;
203  play_level++;
204
205  if (play_level == 1)
206    play_start(buf);
207
208  bsp_interrupt_vector_enable(MM_IRQ_AC97DMAR);
209  return RTEMS_SUCCESSFUL;
210}
211
212static rtems_status_code collect_play(struct snd_buffer **buf)
213{
214  size_t s;
215
216  return rtems_message_queue_receive(
217    play_q_done,
218    buf,
219    &s,
220    RTEMS_WAIT,
221    RTEMS_NO_TIMEOUT
222  );
223}
224
225static rtems_status_code submit_record(struct snd_buffer *buf)
226{
227  bsp_interrupt_vector_disable(MM_IRQ_AC97DMAW);
228  if (record_level == RECORD_Q_SIZE) {
229    bsp_interrupt_vector_enable(MM_IRQ_AC97DMAW);
230    return RTEMS_UNSATISFIED;
231  }
232  record_q[record_produce] = buf;
233  record_produce = (record_produce + 1) & RECORD_Q_MASK;
234  record_level++;
235
236  if (record_level == 1)
237    record_start(buf);
238
239  bsp_interrupt_vector_enable(MM_IRQ_AC97DMAW);
240  return RTEMS_SUCCESSFUL;
241}
242
243static rtems_status_code collect_record(struct snd_buffer **buf)
244{
245  size_t s;
246
247  return rtems_message_queue_receive(
248    record_q_done,
249    buf,
250    &s,
251    RTEMS_WAIT,
252    RTEMS_NO_TIMEOUT
253  );
254}
255
256#define CR_TIMEOUT 10
257
258static int read_cr(unsigned int adr)
259{
260  rtems_status_code sc;
261
262  MM_WRITE(MM_AC97_CRADDR, adr);
263  MM_WRITE(MM_AC97_CRCTL, AC97_CRCTL_RQEN);
264  sc = rtems_semaphore_obtain(cr_write_sem, RTEMS_WAIT, CR_TIMEOUT);
265  if (sc != RTEMS_SUCCESSFUL)
266    return -1;
267  sc = rtems_semaphore_obtain(cr_read_sem, RTEMS_WAIT, CR_TIMEOUT);
268  if (sc != RTEMS_SUCCESSFUL)
269    return -1;
270  return MM_READ(MM_AC97_CRDATAIN);
271}
272
273static int write_cr(unsigned int adr, unsigned int val)
274{
275  rtems_status_code sc;
276
277  MM_WRITE(MM_AC97_CRADDR, adr);
278  MM_WRITE(MM_AC97_CRDATAOUT, val);
279  MM_WRITE(MM_AC97_CRCTL, AC97_CRCTL_RQEN|AC97_CRCTL_WRITE);
280  sc = rtems_semaphore_obtain(cr_write_sem, RTEMS_WAIT, CR_TIMEOUT);
281  if (sc != RTEMS_SUCCESSFUL)
282    return 0;
283  return 1;
284}
285
286rtems_device_driver ac97_open(
287   rtems_device_major_number major,
288   rtems_device_minor_number minor,
289   void *arg
290)
291{
292  int codec_id;
293 
294  if (minor == 0) {
295    /* snd */
296    return RTEMS_SUCCESSFUL;
297  } else {
298    /* mixer */
299    codec_id = read_cr(0x00);
300    if ((codec_id != 0x0d50) && (codec_id != 0x6150)) {
301      printk("AC97 codec detection failed\n");
302      return RTEMS_UNSATISFIED;
303    }
304    write_cr(0x02, 0x0000); /* master volume */
305    write_cr(0x04, 0x0f0f); /* headphones volume */
306    write_cr(0x18, 0x0000); /* PCM out volume */
307    write_cr(0x1c, 0x0f0f); /* record gain */
308
309    write_cr(0x1a, 0x0505); /* record select: stereo mix */
310
311    return RTEMS_SUCCESSFUL;
312  }
313}
314
315static rtems_status_code ioctl_read_channel(void *buf,
316  unsigned int chan, int mono)
317{
318  unsigned int *val = (unsigned int *)buf;
319  int codec;
320  int left, right;
321
322  codec = read_cr(chan);
323  if (codec < 0)
324    return RTEMS_UNSATISFIED;
325  if (codec & 0x8000) {
326    /* muted */
327    *val = 0;
328    return RTEMS_SUCCESSFUL;
329  }
330  if (mono) {
331    right = left = 100-(((codec & 0x1f) + 1)*100)/32;
332  } else {
333    right = 100-(((codec & 0x1f) + 1)*100)/32;
334    left = 100-((((codec & 0x1f00) >> 8) + 1)*100)/32;
335  }
336  *val = left | (right << 8);
337  return RTEMS_SUCCESSFUL;
338}
339
340static rtems_status_code ioctl_write_channel(void *buf,
341  unsigned int chan, int mono)
342{
343  unsigned int *val = (unsigned int *)buf;
344  int left, right;
345  int codec;
346  rtems_status_code sc;
347
348  left = *val & 0xff;
349  left = (left*32)/100 - 1;
350  if(left < 0)
351    left = 0;
352
353  if (mono)
354    right = 31;
355  else {
356    right = (*val >> 8) & 0xff;
357    right = (right*32)/100 - 1;
358    if(right < 0)
359      right = 0;
360  }
361
362  if ((left == 0) && (right == 0))
363    /* mute */
364    codec = 0x8000;
365  else
366    codec = (31-left) | ((31-right) << 8);
367
368  if (!write_cr(chan, codec))
369    sc = RTEMS_UNSATISFIED;
370  else
371    sc = RTEMS_SUCCESSFUL;
372  return sc;
373}
374
375rtems_device_driver ac97_control(
376  rtems_device_major_number major,
377  rtems_device_minor_number minor,
378  void *arg
379)
380{
381  rtems_libio_ioctl_args_t *args = arg;
382  rtems_status_code sc;
383
384  args->ioctl_return = -1;
385  if(minor == 0) {
386    /* dsp */
387    switch (args->command) {
388      case SOUND_SND_SUBMIT_PLAY:
389        return submit_play((struct snd_buffer *)args->buffer);
390      case SOUND_SND_COLLECT_PLAY:
391        return collect_play((struct snd_buffer **)args->buffer);
392      case SOUND_SND_SUBMIT_RECORD:
393        return submit_record((struct snd_buffer *)args->buffer);
394      case SOUND_SND_COLLECT_RECORD:
395        return collect_record((struct snd_buffer **)args->buffer);
396      default:
397        return RTEMS_UNSATISFIED;
398    }
399  } else {
400    /* mixer */
401    switch (args->command) {
402      case SOUND_MIXER_READ(SOUND_MIXER_MIC):
403        sc = ioctl_read_channel(args->buffer, 0x0e, 1);
404        if(sc == RTEMS_SUCCESSFUL)
405          args->ioctl_return = 0;
406        return sc;
407      case SOUND_MIXER_READ(SOUND_MIXER_LINE):
408        sc = ioctl_read_channel(args->buffer, 0x10, 0);
409        if(sc == RTEMS_SUCCESSFUL)
410          args->ioctl_return = 0;
411        return sc;
412      case SOUND_MIXER_WRITE(SOUND_MIXER_MIC):
413        sc = ioctl_write_channel(args->buffer, 0x0e, 1);
414        if(sc == RTEMS_SUCCESSFUL)
415          args->ioctl_return = 0;
416        return sc;
417      case SOUND_MIXER_WRITE(SOUND_MIXER_LINE):
418        sc = ioctl_write_channel(args->buffer, 0x10, 0);
419        if(sc == RTEMS_SUCCESSFUL)
420          args->ioctl_return = 0;
421        return sc;
422      default:
423        return RTEMS_UNSATISFIED;
424    }
425  }
426}
Note: See TracBrowser for help on using the repository browser.