2 * LM4549 Audio Codec Interface
5 * Written by Mathieu Sonet - www.elasticsheep.com
7 * This code is licensed under the GPL.
9 * *****************************************************************
11 * This driver emulates the LM4549 codec.
13 * It supports only one playback voice and no record voice.
17 #include "audio/audio.h"
21 #define LM4549_DEBUG 1
25 #define LM4549_DUMP_DAC_INPUT 1
29 #define DPRINTF(fmt, ...) \
30 do { printf("lm4549: " fmt , ## __VA_ARGS__); } while (0)
32 #define DPRINTF(fmt, ...) do {} while (0)
35 #if defined(LM4549_DUMP_DAC_INPUT)
37 static FILE *fp_dac_input
;
40 /* LM4549 register list */
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
67 static void lm4549_reset(lm4549_state
*s
)
69 uint16_t *regfile
= s
->regfile
;
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;
96 static void lm4549_audio_transfer(lm4549_state
*s
)
98 uint32_t written_bytes
, written_samples
;
101 /* Activate the voice */
102 AUD_set_active_out(s
->voice
, 1);
103 s
->voice_is_active
= 1;
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;
110 #if defined(LM4549_DUMP_DAC_INPUT)
111 fwrite(s
->buffer
, sizeof(uint8_t), written_bytes
, fp_dac_input
);
114 s
->buffer_level
-= written_samples
;
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
];
124 static void lm4549_audio_out_callback(void *opaque
, int free
)
126 lm4549_state
*s
= (lm4549_state
*)opaque
;
127 static uint32_t prev_buffer_level
;
130 int size
= AUD_get_buffer_size_out(s
->voice
);
131 DPRINTF("audio_out_callback size = %i free = %i\n", size
, free
);
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;
140 prev_buffer_level
= s
->buffer_level
;
142 /* Check if a buffer transfer is pending */
143 if (s
->buffer_level
== LM4549_BUFFER_SIZE
) {
144 lm4549_audio_transfer(s
);
146 /* Request more data */
147 if (s
->data_req_cb
!= NULL
) {
148 (s
->data_req_cb
)(s
->opaque
);
153 uint32_t lm4549_read(lm4549_state
*s
, hwaddr offset
)
155 uint16_t *regfile
= s
->regfile
;
158 /* Read the stored value */
159 assert(offset
< 128);
160 value
= regfile
[offset
];
162 DPRINTF("read [0x%02x] = 0x%04x\n", offset
, value
);
167 void lm4549_write(lm4549_state
*s
,
168 hwaddr offset
, uint32_t value
)
170 uint16_t *regfile
= s
->regfile
;
172 assert(offset
< 128);
173 DPRINTF("write [0x%02x] = 0x%04x\n", offset
, value
);
180 case LM4549_PCM_Front_DAC_Rate
:
181 regfile
[LM4549_PCM_Front_DAC_Rate
] = value
;
182 DPRINTF("DAC rate change = %i\n", value
);
184 /* Re-open a voice with the new sample rate */
185 struct audsettings as
;
188 as
.fmt
= AUD_FMT_S16
;
191 s
->voice
= AUD_open_out(
196 lm4549_audio_out_callback
,
201 case LM4549_Powerdown_Ctrl_Stat
:
203 value
|= regfile
[LM4549_Powerdown_Ctrl_Stat
] & 0xf;
204 regfile
[LM4549_Powerdown_Ctrl_Stat
] = value
;
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
);
214 /* Store the new value */
215 regfile
[offset
] = value
;
220 uint32_t lm4549_write_samples(lm4549_state
*s
, uint32_t left
, uint32_t right
)
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.
227 if (s
->buffer_level
> LM4549_BUFFER_SIZE
- 2) {
228 DPRINTF("write_sample Buffer full\n");
232 /* Store 16-bit samples in the buffer */
233 s
->buffer
[s
->buffer_level
++] = (left
>> 4);
234 s
->buffer
[s
->buffer_level
++] = (right
>> 4);
236 if (s
->buffer_level
== LM4549_BUFFER_SIZE
) {
237 /* Trigger the transfer of the buffer to the audio host */
238 lm4549_audio_transfer(s
);
244 static int lm4549_post_load(void *opaque
, int version_id
)
246 lm4549_state
*s
= (lm4549_state
*)opaque
;
247 uint16_t *regfile
= s
->regfile
;
249 /* Re-open a voice with the current sample rate */
250 uint32_t freq
= regfile
[LM4549_PCM_Front_DAC_Rate
];
252 DPRINTF("post_load freq = %i\n", freq
);
253 DPRINTF("post_load voice_is_active = %i\n", s
->voice_is_active
);
255 struct audsettings as
;
258 as
.fmt
= AUD_FMT_S16
;
261 s
->voice
= AUD_open_out(
266 lm4549_audio_out_callback
,
271 if (s
->voice_is_active
== 1) {
272 lm4549_audio_out_callback(s
, AUD_get_buffer_size_out(s
->voice
));
278 void lm4549_init(lm4549_state
*s
, lm4549_callback data_req_cb
, void* opaque
)
280 struct audsettings as
;
282 /* Store the callback and opaque pointer */
283 s
->data_req_cb
= data_req_cb
;
286 /* Init the registers */
289 /* Register an audio card */
290 AUD_register_card("lm4549", &s
->card
);
292 /* Open a default voice */
295 as
.fmt
= AUD_FMT_S16
;
298 s
->voice
= AUD_open_out(
303 lm4549_audio_out_callback
,
307 AUD_set_volume_out(s
->voice
, 0, 255, 255);
309 s
->voice_is_active
= 0;
311 /* Reset the input buffer */
312 memset(s
->buffer
, 0x00, sizeof(s
->buffer
));
315 #if defined(LM4549_DUMP_DAC_INPUT)
316 fp_dac_input
= fopen("lm4549_dac_input.pcm", "wb");
318 hw_error("Unable to open lm4549_dac_input.pcm for writing\n");
323 const VMStateDescription vmstate_lm4549_state
= {
324 .name
= "lm4549_state",
326 .minimum_version_id
= 1,
327 .post_load
= lm4549_post_load
,
328 .fields
= (VMStateField
[]) {
329 VMSTATE_UINT32(voice_is_active
, lm4549_state
),
330 VMSTATE_UINT16_ARRAY(regfile
, lm4549_state
, 128),
331 VMSTATE_UINT16_ARRAY(buffer
, lm4549_state
, LM4549_BUFFER_SIZE
),
332 VMSTATE_UINT32(buffer_level
, lm4549_state
),
333 VMSTATE_END_OF_LIST()