Don't look up pid/tid on YAGL_LOG_FUNC_SET
[sdk/emulator/qemu.git] / tizen / src / hw / pci / maru_brillcodec_device.c
1 /*
2  * Virtual Codec Device
3  *
4  * Copyright (c) 2013 - 2014 Samsung Electronics Co., Ltd All Rights Reserved
5  *
6  * Contact:
7  *  Kitae Kim <kt920.kim@samsung.com>
8  *  SeokYeon Hwang <syeon.hwang@samsung.com>
9  *  YeongKyoon Lee <yeongkyoon.lee@samsung.com>
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * as published by the Free Software Foundation; either version 2
14  * of the License, or (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
24  * MA 02110-1301, USA.
25  *
26  * Contributors:
27  * - S-Core Co., Ltd
28  *
29  */
30
31 #define SUPPORT_MEMORY_MONOPOLIZING
32 //#define ENCODE_VIDEO_USE_MEMORY_MONOPOLIZING
33
34 #include "qemu/osdep.h"
35 #include "qemu/main-loop.h"
36 #include "qapi/error.h"
37 #include "hw/pci/pci.h"
38
39 #include "hw/maru_device_ids.h"
40 #include "util/osutil.h"
41 #include "maru_brillcodec.h"
42 #include "util/new_debug_ch.h"
43
44 /* define debug channel */
45 DECLARE_DEBUG_CHANNEL(brillcodec);
46
47 #define CODEC_DEVICE_NAME   "codec-pci"
48 #define CODEC_DEVICE_THREAD "codec-workthread"
49
50 #define CODEC_MAJOR_VERSION         3
51 #define CODEC_MINOR_VERSION         0
52
53 #define CODEC_REG_SIZE              (256)
54 #define DEFAULT_WORKER_THREAD_CNT   8
55
56 enum device_cmd {
57     DEVICE_CMD_API_INDEX             = 0,
58     DEVICE_CMD_CONTEXT_INDEX,
59     DEVICE_CMD_DEVICE_MEM_OFFSET,
60     DEVICE_CMD_GET_THREAD_STATE,
61     DEVICE_CMD_GET_CTX_FROM_QUEUE,
62     DEVICE_CMD_GET_DATA_FROM_QUEUE,
63     DEVICE_CMD_RELEASE_CONTEXT,
64     DEVICE_CMD_GET_ELEMENT,
65     DEVICE_CMD_GET_CONTEXT_INDEX,
66     DEVICE_CMD_GET_DEVICE_LOG_INFO,
67     DEVICE_CMD_GET_PROFILE_STATUS,
68 };
69
70 enum thread_state {
71     CODEC_TASK_START    = 0,
72     CODEC_TASK_END      = 0x1f,
73 };
74
75 static void brillcodec_threads_create(MaruBrillCodecState *s)
76 {
77     int index;
78     QemuThread *pthread = NULL;
79
80     LOG_TRACE("enter: %s\n", __func__);
81
82     pthread = g_malloc(sizeof(QemuThread) * s->worker_thread_cnt);
83     if (!pthread) {
84         LOG_SEVERE("failed to allocate threadpool memory.\n");
85         return;
86     }
87
88     qemu_cond_init(&s->threadpool.cond);
89     qemu_mutex_init(&s->threadpool.mutex);
90
91     s->is_thread_running = true;
92
93     qemu_mutex_lock(&s->context_mutex);
94     s->idle_thread_cnt = 0;
95     qemu_mutex_unlock(&s->context_mutex);
96
97     for (index = 0; index < s->worker_thread_cnt; index++) {
98         qemu_thread_create(&pthread[index], CODEC_DEVICE_THREAD,
99             brillcodec_threads, (void *)s, QEMU_THREAD_JOINABLE);
100     }
101
102     s->threadpool.threads = pthread;
103
104     LOG_TRACE("leave: %s\n", __func__);
105 }
106
107 static void brillcodec_get_cpu_cores(MaruBrillCodecState *s)
108 {
109     s->worker_thread_cnt = get_number_of_processors();
110     if (s->worker_thread_cnt < DEFAULT_WORKER_THREAD_CNT) {
111         s->worker_thread_cnt = DEFAULT_WORKER_THREAD_CNT;
112     }
113
114     LOG_TRACE("number of threads: %d\n", s->worker_thread_cnt);
115 }
116
117 static void brillcodec_bh_callback(void *opaque)
118 {
119     MaruBrillCodecState *s = (MaruBrillCodecState *)opaque;
120
121     LOG_TRACE("enter: %s\n", __func__);
122
123     qemu_mutex_lock(&s->context_queue_mutex);
124     if (!QTAILQ_EMPTY(&codec_wq)) {
125         qemu_mutex_unlock(&s->context_queue_mutex);
126
127         LOG_TRACE("raise irq\n");
128         pci_set_irq(&s->dev, 1);
129         s->irq_raised = 1;
130     } else {
131         qemu_mutex_unlock(&s->context_queue_mutex);
132         LOG_TRACE("codec_wq is empty!!\n");
133     }
134
135     LOG_TRACE("leave: %s\n", __func__);
136 }
137
138 static uint64_t brillcodec_read(void *opaque,
139                                         hwaddr addr,
140                                         unsigned size)
141 {
142     MaruBrillCodecState *s = (MaruBrillCodecState *)opaque;
143     uint64_t ret = 0;
144
145     switch (addr >> 2) {
146     case DEVICE_CMD_GET_THREAD_STATE:
147         qemu_mutex_lock(&s->context_queue_mutex);
148         if (s->irq_raised) {
149             ret = CODEC_TASK_END;
150             pci_set_irq(&s->dev, 0);
151             s->irq_raised = 0;
152         }
153         qemu_mutex_unlock(&s->context_queue_mutex);
154
155         LOG_TRACE("get thread_state. ret: %d\n", ret);
156         break;
157
158     case DEVICE_CMD_GET_CTX_FROM_QUEUE:
159     {
160         DeviceMemEntry *head = NULL;
161
162         qemu_mutex_lock(&s->context_queue_mutex);
163         head = QTAILQ_FIRST(&codec_wq);
164         if (head) {
165             ret = head->ctx_id;
166             QTAILQ_REMOVE(&codec_wq, head, node);
167             entry[ret] = head;
168             LOG_TRACE("get a elem from codec_wq. 0x%x\n", head);
169         } else {
170             ret = 0;
171         }
172         qemu_mutex_unlock(&s->context_queue_mutex);
173
174         LOG_TRACE("get a head from a writequeue. head: %x\n", ret);
175     }
176         break;
177
178     case DEVICE_CMD_GET_DEVICE_LOG_INFO:
179         ret |= CODEC_MAJOR_VERSION << 8;
180         ret |= CODEC_MINOR_VERSION;
181         LOG_TRACE("codec version: %d.%d.%d\n", CODEC_MAJOR_VERSION, CODEC_MINOR_VERSION, 0);
182
183         ret |= s->memory_monopolizing << 16;
184         break;
185
186     case DEVICE_CMD_GET_ELEMENT:
187         ret = brillcodec_query_list(s);
188         break;
189
190     case DEVICE_CMD_GET_CONTEXT_INDEX:
191         ret = brillcodec_get_context_index(s);
192         LOG_TRACE("get context index: %d\n", ret);
193         break;
194
195     case DEVICE_CMD_GET_PROFILE_STATUS:
196         ret = s->profile;
197         LOG_TRACE("profile status: %d\n", s->profile);
198         break;
199
200     default:
201         LOG_SEVERE("no avaiable command for read. %d\n", addr);
202     }
203
204     return ret;
205 }
206
207 static void brillcodec_write(void *opaque, hwaddr addr,
208                                     uint64_t value, unsigned size)
209 {
210     MaruBrillCodecState *s = (MaruBrillCodecState *)opaque;
211
212     switch (addr >> 2) {
213     case DEVICE_CMD_API_INDEX:
214         LOG_TRACE("set codec_cmd value: %d\n", value);
215         s->ioparam.api_index = value;
216         brillcodec_wakeup_threads(s, value);
217         break;
218
219     case DEVICE_CMD_CONTEXT_INDEX:
220         LOG_TRACE("set context_index value: %d\n", value);
221         s->ioparam.ctx_index = value;
222         break;
223
224     case DEVICE_CMD_DEVICE_MEM_OFFSET:
225         LOG_TRACE("set mem_offset value: 0x%x\n", value);
226         s->ioparam.mem_offset = value;
227         break;
228
229     case DEVICE_CMD_RELEASE_CONTEXT:
230     {
231         int ctx_id = (int32_t)value;
232
233         if (CONTEXT(s, ctx_id)->occupied_thread) {
234             CONTEXT(s, ctx_id)->requested_close = true;
235             LOG_INFO("make running thread to handle deinit\n");
236         } else {
237             brillcodec_release_context(s, ctx_id);
238         }
239     }
240         break;
241
242     case DEVICE_CMD_GET_DATA_FROM_QUEUE:
243         brillcodec_pop_writequeue(s, (uint32_t)value);
244         break;
245
246     default:
247         LOG_SEVERE("no available command for write. %d\n", addr);
248     }
249 }
250
251 static const MemoryRegionOps brillcodec_mmio_ops = {
252     .read = brillcodec_read,
253     .write = brillcodec_write,
254     .valid = {
255         .min_access_size = 4,
256         .max_access_size = 4,
257         .unaligned = false
258     },
259     .endianness = DEVICE_LITTLE_ENDIAN,
260 };
261
262 static int brillcodec_initfn(PCIDevice *dev)
263 {
264     MaruBrillCodecState *s = DO_UPCAST(MaruBrillCodecState, dev, dev);
265     uint8_t *pci_conf = s->dev.config;
266
267     LOG_INFO("device initialization.\n");
268     LOG_INFO("version: %d.%d.%d\n", CODEC_MAJOR_VERSION, CODEC_MINOR_VERSION, 0);
269
270     pci_config_set_interrupt_pin(pci_conf, 1);
271
272     memory_region_init_ram(&s->vram, OBJECT(s), "maru_brill_codec.vram", CODEC_MEM_SIZE, &error_abort);
273     s->vaddr = (uint8_t *)memory_region_get_ram_ptr(&s->vram);
274
275     memory_region_init_io(&s->mmio, OBJECT(s), &brillcodec_mmio_ops, s,
276                         "maru_brill_codec.mmio", CODEC_REG_SIZE);
277
278     pci_register_bar(&s->dev, 0, PCI_BASE_ADDRESS_MEM_PREFETCH, &s->vram);
279     pci_register_bar(&s->dev, 1, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mmio);
280
281     qemu_mutex_init(&s->context_mutex);
282     qemu_mutex_init(&s->context_queue_mutex);
283     qemu_mutex_init(&s->ioparam_queue_mutex);
284
285     brillcodec_get_cpu_cores(s);
286     brillcodec_threads_create(s);
287
288     // register a function to qemu bottom-halves to switch context.
289     s->codec_bh = qemu_bh_new(brillcodec_bh_callback, s);
290
291     // register plugins
292     if ((s->hwaccel_plugin = probe_plugin())) {
293         LOG_INFO("%s extension is enabled.\n", s->hwaccel_plugin->name);
294     }
295
296     // configurations
297     s->memory_monopolizing = 0;
298 #ifdef SUPPORT_MEMORY_MONOPOLIZING
299 # ifdef ENCODE_VIDEO_USE_MEMORY_MONOPOLIZING
300     s->memory_monopolizing |= 1 << ENCODE_VIDEO;
301     LOG_INFO("API [%d] use memory monopolizing.\n", ENCODE_VIDEO);
302 # endif
303 #endif
304
305     if (s->profile) {
306         LOG_INFO("Profile the brillcodec.(%d)\n", s->profile);
307     }
308
309     return 0;
310 }
311
312 static void brillcodec_exitfn(PCIDevice *dev)
313 {
314     MaruBrillCodecState *s = DO_UPCAST(MaruBrillCodecState, dev, dev);
315     LOG_INFO("device exit\n");
316
317     qemu_mutex_destroy(&s->context_mutex);
318     qemu_mutex_destroy(&s->context_queue_mutex);
319     qemu_mutex_destroy(&s->ioparam_queue_mutex);
320
321     qemu_bh_delete(s->codec_bh);
322 }
323
324 static void brillcodec_reset(DeviceState *d)
325 {
326     MaruBrillCodecState *s = (MaruBrillCodecState *)d;
327     LOG_INFO("device reset\n");
328
329     s->irq_raised = 0;
330
331     memset(&s->context, 0, sizeof(CodecContext) * CODEC_CONTEXT_MAX);
332     memset(&s->ioparam, 0, sizeof(CodecParam));
333 }
334
335 static Property brillcodec_props[] = {
336     DEFINE_PROP_UINT8("profile", MaruBrillCodecState, profile, 0),
337     DEFINE_PROP_END_OF_LIST(),
338 };
339
340 static void brillcodec_class_init(ObjectClass *klass, void *data)
341 {
342     DeviceClass *dc = DEVICE_CLASS(klass);
343     PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
344
345     k->init = brillcodec_initfn;
346     k->exit = brillcodec_exitfn;
347     k->vendor_id = PCI_VENDOR_ID_TIZEN;
348     k->device_id = PCI_DEVICE_ID_VIRTUAL_BRILL_CODEC;
349     k->class_id = PCI_CLASS_OTHERS;
350     dc->reset = brillcodec_reset;
351     dc->props = brillcodec_props;
352     dc->desc = "Virtual new codec device for Tizen emulator";
353 }
354
355 static TypeInfo codec_device_info = {
356     .name          = CODEC_DEVICE_NAME,
357     .parent        = TYPE_PCI_DEVICE,
358     .instance_size = sizeof(MaruBrillCodecState),
359     .class_init    = brillcodec_class_init,
360 };
361
362 static void codec_register_types(void)
363 {
364     type_register_static(&codec_device_info);
365 }
366
367 type_init(codec_register_types)