sync with tizen_2.2
[sdk/emulator/qemu.git] / hw / exynos4210_audio.c
1 /*
2  * Samsung exynos4210 Audio driver
3  *
4  * Copyright (c) 2012 Samsung Electronics Co., Ltd.
5  *  Vorobiov Stanislav <s.vorobiov@samsung.com>
6  *
7  *  This program is free software; you can redistribute it and/or modify it
8  *  under the terms of the GNU General Public License as published by the
9  *  Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful, but WITHOUT
13  *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14  *  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15  *  for more details.
16  *
17  *  You should have received a copy of the GNU General Public License along
18  *  with this program; if not, see <http://www.gnu.org/licenses/>.
19  *
20  */
21
22 #include "exynos4210_i2s.h"
23 #include "wm8994.h"
24
25 /* #define DEBUG_EXYNOS4210_AUDIO */
26
27 #ifdef DEBUG_EXYNOS4210_AUDIO
28 #define DPRINTF(fmt, ...) \
29     do { \
30         fprintf(stdout, "AUDIO: [%s:%d] " fmt, __func__, __LINE__, \
31             ## __VA_ARGS__); \
32     } while (0)
33 #else
34 #define DPRINTF(fmt, ...) do {} while (0)
35 #endif
36
37 #define AUDIO_MAX_WORDS (4096 / EXYNOS4210_I2S_WORD_LEN)
38
39 typedef struct {
40     Exynos4210I2SSlave i2s;
41
42     DeviceState *wm8994;
43 } Exynos4210AudioState;
44
45 static void exynos4210_audio_callback(void *opaque, int free_out_bytes)
46 {
47     Exynos4210AudioState *s = (Exynos4210AudioState *)opaque;
48     uint8_t buff[AUDIO_MAX_WORDS * EXYNOS4210_I2S_WORD_LEN];
49     int free_out_words = free_out_bytes / EXYNOS4210_I2S_WORD_LEN;
50     uint32_t num_words;
51
52     if (free_out_words <= 0) {
53         return;
54     }
55
56     if (free_out_words > AUDIO_MAX_WORDS) {
57         free_out_words = AUDIO_MAX_WORDS;
58     }
59
60     if (!exynos4210_i2s_dma_enabled(qdev_get_parent_bus(&s->i2s.qdev))) {
61         return;
62     }
63
64     num_words = exynos4210_i2s_dma_get_words_available(
65                     qdev_get_parent_bus(&s->i2s.qdev));
66
67     num_words = MIN(num_words, free_out_words);
68
69     exynos4210_i2s_dma_read(qdev_get_parent_bus(&s->i2s.qdev),
70                             &buff[0],
71                             num_words);
72
73     num_words = wm8994_dac_write(s->wm8994,
74         &buff[0],
75         num_words * EXYNOS4210_I2S_WORD_LEN) / EXYNOS4210_I2S_WORD_LEN;
76
77     exynos4210_i2s_dma_advance(qdev_get_parent_bus(&s->i2s.qdev), num_words);
78 }
79
80 static void exynos4210_audio_dma_enable(Exynos4210I2SSlave *i2s, bool enable)
81 {
82     Exynos4210AudioState *s =
83         FROM_EXYNOS4210_I2S_SLAVE(Exynos4210AudioState, i2s);
84
85     DPRINTF("enter %d\n", enable);
86
87     wm8994_set_active(s->wm8994, enable);
88 }
89
90 static void exynos4210_audio_reset(DeviceState *dev)
91 {
92     DPRINTF("enter\n");
93 }
94
95 static int exynos4210_audio_init(Exynos4210I2SSlave *i2s)
96 {
97     Exynos4210AudioState *s =
98         FROM_EXYNOS4210_I2S_SLAVE(Exynos4210AudioState, i2s);
99
100     wm8994_data_req_set(s->wm8994, exynos4210_audio_callback, s);
101
102     return 0;
103 }
104
105 static const VMStateDescription vmstate_exynos4210_audio = {
106     .name = "exynos4210.audio",
107     .version_id = 1,
108     .minimum_version_id = 1,
109     .minimum_version_id_old = 1,
110     .fields = (VMStateField[]) {
111         VMSTATE_EXYNOS4210_I2S_SLAVE(i2s, Exynos4210AudioState),
112         VMSTATE_END_OF_LIST()
113     }
114 };
115
116 static Property exynos4210_audio_properties[] = {
117     {
118         .name   = "wm8994",
119         .info   = &qdev_prop_ptr,
120         .offset = offsetof(Exynos4210AudioState, wm8994),
121     },
122     DEFINE_PROP_END_OF_LIST(),
123 };
124
125 static void exynos4210_audio_class_init(ObjectClass *klass, void *data)
126 {
127     DeviceClass *dc = DEVICE_CLASS(klass);
128     Exynos4210I2SSlaveClass *sc = EXYNOS4210_I2S_SLAVE_CLASS(klass);
129
130     sc->init = exynos4210_audio_init;
131     sc->dma_enable = exynos4210_audio_dma_enable;
132     dc->reset = exynos4210_audio_reset;
133     dc->props = exynos4210_audio_properties;
134     dc->vmsd = &vmstate_exynos4210_audio;
135 }
136
137 static TypeInfo exynos4210_audio_info = {
138     .name          = "exynos4210.audio",
139     .parent        = TYPE_EXYNOS4210_I2S_SLAVE,
140     .instance_size = sizeof(Exynos4210AudioState),
141     .class_init    = exynos4210_audio_class_init,
142 };
143
144 static void exynos4210_audio_register_types(void)
145 {
146     type_register_static(&exynos4210_audio_info);
147 }
148
149 type_init(exynos4210_audio_register_types)