1 /* 2 * LM4549 Audio Codec Interface 3 * 4 * Copyright (c) 2011 5 * Written by Mathieu Sonet - www.elasticsheep.com 6 * 7 * This code is licensed 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/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, hwaddr 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 hwaddr 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 - 2) { 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 }; 337