Annotation of qemu/hw/lm4549.c, revision 1.1.1.1

1.1       root        1: /*
                      2:  * LM4549 Audio Codec Interface
                      3:  *
                      4:  * Copyright (c) 2011
                      5:  * Written by Mathieu Sonet - www.elasticsheep.com
                      6:  *
                      7:  * This code is licenced under the GPL.
                      8:  *
                      9:  * *****************************************************************
                     10:  *
                     11:  * This driver emulates the LM4549 codec.
                     12:  *
                     13:  * It supports only one playback voice and no record voice.
                     14:  */
                     15: 
                     16: #include "hw.h"
                     17: #include "audio/audio.h"
                     18: #include "lm4549.h"
                     19: 
                     20: #if 0
                     21: #define LM4549_DEBUG  1
                     22: #endif
                     23: 
                     24: #if 0
                     25: #define LM4549_DUMP_DAC_INPUT 1
                     26: #endif
                     27: 
                     28: #ifdef LM4549_DEBUG
                     29: #define DPRINTF(fmt, ...) \
                     30: do { printf("lm4549: " fmt , ## __VA_ARGS__); } while (0)
                     31: #else
                     32: #define DPRINTF(fmt, ...) do {} while (0)
                     33: #endif
                     34: 
                     35: #if defined(LM4549_DUMP_DAC_INPUT)
                     36: #include <stdio.h>
                     37: static FILE *fp_dac_input;
                     38: #endif
                     39: 
                     40: /* LM4549 register list */
                     41: enum {
                     42:     LM4549_Reset                    = 0x00,
                     43:     LM4549_Master_Volume            = 0x02,
                     44:     LM4549_Line_Out_Volume          = 0x04,
                     45:     LM4549_Master_Volume_Mono       = 0x06,
                     46:     LM4549_PC_Beep_Volume           = 0x0A,
                     47:     LM4549_Phone_Volume             = 0x0C,
                     48:     LM4549_Mic_Volume               = 0x0E,
                     49:     LM4549_Line_In_Volume           = 0x10,
                     50:     LM4549_CD_Volume                = 0x12,
                     51:     LM4549_Video_Volume             = 0x14,
                     52:     LM4549_Aux_Volume               = 0x16,
                     53:     LM4549_PCM_Out_Volume           = 0x18,
                     54:     LM4549_Record_Select            = 0x1A,
                     55:     LM4549_Record_Gain              = 0x1C,
                     56:     LM4549_General_Purpose          = 0x20,
                     57:     LM4549_3D_Control               = 0x22,
                     58:     LM4549_Powerdown_Ctrl_Stat      = 0x26,
                     59:     LM4549_Ext_Audio_ID             = 0x28,
                     60:     LM4549_Ext_Audio_Stat_Ctrl      = 0x2A,
                     61:     LM4549_PCM_Front_DAC_Rate       = 0x2C,
                     62:     LM4549_PCM_ADC_Rate             = 0x32,
                     63:     LM4549_Vendor_ID1               = 0x7C,
                     64:     LM4549_Vendor_ID2               = 0x7E
                     65: };
                     66: 
                     67: static void lm4549_reset(lm4549_state *s)
                     68: {
                     69:     uint16_t *regfile = s->regfile;
                     70: 
                     71:     regfile[LM4549_Reset]               = 0x0d50;
                     72:     regfile[LM4549_Master_Volume]       = 0x8008;
                     73:     regfile[LM4549_Line_Out_Volume]     = 0x8000;
                     74:     regfile[LM4549_Master_Volume_Mono]  = 0x8000;
                     75:     regfile[LM4549_PC_Beep_Volume]      = 0x0000;
                     76:     regfile[LM4549_Phone_Volume]        = 0x8008;
                     77:     regfile[LM4549_Mic_Volume]          = 0x8008;
                     78:     regfile[LM4549_Line_In_Volume]      = 0x8808;
                     79:     regfile[LM4549_CD_Volume]           = 0x8808;
                     80:     regfile[LM4549_Video_Volume]        = 0x8808;
                     81:     regfile[LM4549_Aux_Volume]          = 0x8808;
                     82:     regfile[LM4549_PCM_Out_Volume]      = 0x8808;
                     83:     regfile[LM4549_Record_Select]       = 0x0000;
                     84:     regfile[LM4549_Record_Gain]         = 0x8000;
                     85:     regfile[LM4549_General_Purpose]     = 0x0000;
                     86:     regfile[LM4549_3D_Control]          = 0x0101;
                     87:     regfile[LM4549_Powerdown_Ctrl_Stat] = 0x000f;
                     88:     regfile[LM4549_Ext_Audio_ID]        = 0x0001;
                     89:     regfile[LM4549_Ext_Audio_Stat_Ctrl] = 0x0000;
                     90:     regfile[LM4549_PCM_Front_DAC_Rate]  = 0xbb80;
                     91:     regfile[LM4549_PCM_ADC_Rate]        = 0xbb80;
                     92:     regfile[LM4549_Vendor_ID1]          = 0x4e53;
                     93:     regfile[LM4549_Vendor_ID2]          = 0x4331;
                     94: }
                     95: 
                     96: static void lm4549_audio_transfer(lm4549_state *s)
                     97: {
                     98:     uint32_t written_bytes, written_samples;
                     99:     uint32_t i;
                    100: 
                    101:     /* Activate the voice */
                    102:     AUD_set_active_out(s->voice, 1);
                    103:     s->voice_is_active = 1;
                    104: 
                    105:     /* Try to write the buffer content */
                    106:     written_bytes = AUD_write(s->voice, s->buffer,
                    107:                               s->buffer_level * sizeof(uint16_t));
                    108:     written_samples = written_bytes >> 1;
                    109: 
                    110: #if defined(LM4549_DUMP_DAC_INPUT)
                    111:     fwrite(s->buffer, sizeof(uint8_t), written_bytes, fp_dac_input);
                    112: #endif
                    113: 
                    114:     s->buffer_level -= written_samples;
                    115: 
                    116:     if (s->buffer_level > 0) {
                    117:         /* Move the data back to the start of the buffer */
                    118:         for (i = 0; i < s->buffer_level; i++) {
                    119:             s->buffer[i] = s->buffer[i + written_samples];
                    120:         }
                    121:     }
                    122: }
                    123: 
                    124: static void lm4549_audio_out_callback(void *opaque, int free)
                    125: {
                    126:     lm4549_state *s = (lm4549_state *)opaque;
                    127:     static uint32_t prev_buffer_level;
                    128: 
                    129: #ifdef LM4549_DEBUG
                    130:     int size = AUD_get_buffer_size_out(s->voice);
                    131:     DPRINTF("audio_out_callback size = %i free = %i\n", size, free);
                    132: #endif
                    133: 
                    134:     /* Detect that no data are consumed
                    135:        => disable the voice */
                    136:     if (s->buffer_level == prev_buffer_level) {
                    137:         AUD_set_active_out(s->voice, 0);
                    138:         s->voice_is_active = 0;
                    139:     }
                    140:     prev_buffer_level = s->buffer_level;
                    141: 
                    142:     /* Check if a buffer transfer is pending */
                    143:     if (s->buffer_level == LM4549_BUFFER_SIZE) {
                    144:         lm4549_audio_transfer(s);
                    145: 
                    146:         /* Request more data */
                    147:         if (s->data_req_cb != NULL) {
                    148:             (s->data_req_cb)(s->opaque);
                    149:         }
                    150:     }
                    151: }
                    152: 
                    153: uint32_t lm4549_read(lm4549_state *s, target_phys_addr_t offset)
                    154: {
                    155:     uint16_t *regfile = s->regfile;
                    156:     uint32_t value = 0;
                    157: 
                    158:     /* Read the stored value */
                    159:     assert(offset < 128);
                    160:     value = regfile[offset];
                    161: 
                    162:     DPRINTF("read [0x%02x] = 0x%04x\n", offset, value);
                    163: 
                    164:     return value;
                    165: }
                    166: 
                    167: void lm4549_write(lm4549_state *s,
                    168:                   target_phys_addr_t offset, uint32_t value)
                    169: {
                    170:     uint16_t *regfile = s->regfile;
                    171: 
                    172:     assert(offset < 128);
                    173:     DPRINTF("write [0x%02x] = 0x%04x\n", offset, value);
                    174: 
                    175:     switch (offset) {
                    176:     case LM4549_Reset:
                    177:         lm4549_reset(s);
                    178:         break;
                    179: 
                    180:     case LM4549_PCM_Front_DAC_Rate:
                    181:         regfile[LM4549_PCM_Front_DAC_Rate] = value;
                    182:         DPRINTF("DAC rate change = %i\n", value);
                    183: 
                    184:         /* Re-open a voice with the new sample rate */
                    185:         struct audsettings as;
                    186:         as.freq = value;
                    187:         as.nchannels = 2;
                    188:         as.fmt = AUD_FMT_S16;
                    189:         as.endianness = 0;
                    190: 
                    191:         s->voice = AUD_open_out(
                    192:             &s->card,
                    193:             s->voice,
                    194:             "lm4549.out",
                    195:             s,
                    196:             lm4549_audio_out_callback,
                    197:             &as
                    198:         );
                    199:         break;
                    200: 
                    201:     case LM4549_Powerdown_Ctrl_Stat:
                    202:         value &= ~0xf;
                    203:         value |= regfile[LM4549_Powerdown_Ctrl_Stat] & 0xf;
                    204:         regfile[LM4549_Powerdown_Ctrl_Stat] = value;
                    205:         break;
                    206: 
                    207:     case LM4549_Ext_Audio_ID:
                    208:     case LM4549_Vendor_ID1:
                    209:     case LM4549_Vendor_ID2:
                    210:         DPRINTF("Write to read-only register 0x%x\n", (int)offset);
                    211:         break;
                    212: 
                    213:     default:
                    214:         /* Store the new value */
                    215:         regfile[offset] = value;
                    216:         break;
                    217:     }
                    218: }
                    219: 
                    220: uint32_t lm4549_write_samples(lm4549_state *s, uint32_t left, uint32_t right)
                    221: {
                    222:     /* The left and right samples are in 20-bit resolution.
                    223:        The LM4549 has 18-bit resolution and only uses the bits [19:2].
                    224:        This model supports 16-bit playback.
                    225:     */
                    226: 
                    227:     if (s->buffer_level >= LM4549_BUFFER_SIZE) {
                    228:         DPRINTF("write_sample Buffer full\n");
                    229:         return 0;
                    230:     }
                    231: 
                    232:     /* Store 16-bit samples in the buffer */
                    233:     s->buffer[s->buffer_level++] = (left >> 4);
                    234:     s->buffer[s->buffer_level++] = (right >> 4);
                    235: 
                    236:     if (s->buffer_level == LM4549_BUFFER_SIZE) {
                    237:         /* Trigger the transfer of the buffer to the audio host */
                    238:         lm4549_audio_transfer(s);
                    239:     }
                    240: 
                    241:     return 1;
                    242: }
                    243: 
                    244: static int lm4549_post_load(void *opaque, int version_id)
                    245: {
                    246:     lm4549_state *s = (lm4549_state *)opaque;
                    247:     uint16_t *regfile = s->regfile;
                    248: 
                    249:     /* Re-open a voice with the current sample rate */
                    250:     uint32_t freq = regfile[LM4549_PCM_Front_DAC_Rate];
                    251: 
                    252:     DPRINTF("post_load freq = %i\n", freq);
                    253:     DPRINTF("post_load voice_is_active = %i\n", s->voice_is_active);
                    254: 
                    255:     struct audsettings as;
                    256:     as.freq = freq;
                    257:     as.nchannels = 2;
                    258:     as.fmt = AUD_FMT_S16;
                    259:     as.endianness = 0;
                    260: 
                    261:     s->voice = AUD_open_out(
                    262:         &s->card,
                    263:         s->voice,
                    264:         "lm4549.out",
                    265:         s,
                    266:         lm4549_audio_out_callback,
                    267:         &as
                    268:     );
                    269: 
                    270:     /* Request data */
                    271:     if (s->voice_is_active == 1) {
                    272:         lm4549_audio_out_callback(s, AUD_get_buffer_size_out(s->voice));
                    273:     }
                    274: 
                    275:     return 0;
                    276: }
                    277: 
                    278: void lm4549_init(lm4549_state *s, lm4549_callback data_req_cb, void* opaque)
                    279: {
                    280:     struct audsettings as;
                    281: 
                    282:     /* Store the callback and opaque pointer */
                    283:     s->data_req_cb = data_req_cb;
                    284:     s->opaque = opaque;
                    285: 
                    286:     /* Init the registers */
                    287:     lm4549_reset(s);
                    288: 
                    289:     /* Register an audio card */
                    290:     AUD_register_card("lm4549", &s->card);
                    291: 
                    292:     /* Open a default voice */
                    293:     as.freq = 48000;
                    294:     as.nchannels = 2;
                    295:     as.fmt = AUD_FMT_S16;
                    296:     as.endianness = 0;
                    297: 
                    298:     s->voice = AUD_open_out(
                    299:         &s->card,
                    300:         s->voice,
                    301:         "lm4549.out",
                    302:         s,
                    303:         lm4549_audio_out_callback,
                    304:         &as
                    305:     );
                    306: 
                    307:     AUD_set_volume_out(s->voice, 0, 255, 255);
                    308: 
                    309:     s->voice_is_active = 0;
                    310: 
                    311:     /* Reset the input buffer */
                    312:     memset(s->buffer, 0x00, sizeof(s->buffer));
                    313:     s->buffer_level = 0;
                    314: 
                    315: #if defined(LM4549_DUMP_DAC_INPUT)
                    316:     fp_dac_input = fopen("lm4549_dac_input.pcm", "wb");
                    317:     if (!fp_dac_input) {
                    318:         hw_error("Unable to open lm4549_dac_input.pcm for writing\n");
                    319:     }
                    320: #endif
                    321: }
                    322: 
                    323: const VMStateDescription vmstate_lm4549_state = {
                    324:     .name = "lm4549_state",
                    325:     .version_id = 1,
                    326:     .minimum_version_id = 1,
                    327:     .minimum_version_id_old = 1,
                    328:     .post_load = &lm4549_post_load,
                    329:     .fields      = (VMStateField[]) {
                    330:         VMSTATE_UINT32(voice_is_active, lm4549_state),
                    331:         VMSTATE_UINT16_ARRAY(regfile, lm4549_state, 128),
                    332:         VMSTATE_UINT16_ARRAY(buffer, lm4549_state, LM4549_BUFFER_SIZE),
                    333:         VMSTATE_UINT32(buffer_level, lm4549_state),
                    334:         VMSTATE_END_OF_LIST()
                    335:     }
                    336: };

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.