Merge 'v2.2.0' into tizen_next_qemu_2.2
[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/main-loop.h"
35 #include "hw/pci/pci.h"
36
37 #include "hw/maru_device_ids.h"
38 #include "util/osutil.h"
39 #include "maru_brillcodec.h"
40 #include "debug_ch.h"
41
42 /* define debug channel */
43 MULTI_DEBUG_CHANNEL(qemu, brillcodec);
44
45 #define CODEC_DEVICE_NAME   "codec-pci"
46 #define CODEC_DEVICE_THREAD "codec-workthread"
47
48 #define CODEC_MAJOR_VERSION         3
49 #define CODEC_MINOR_VERSION         0
50
51 #define CODEC_REG_SIZE              (256)
52 #define DEFAULT_WORKER_THREAD_CNT   8
53
54 enum device_cmd {
55     DEVICE_CMD_API_INDEX             = 0,
56     DEVICE_CMD_CONTEXT_INDEX,
57     DEVICE_CMD_DEVICE_MEM_OFFSET,
58     DEVICE_CMD_GET_THREAD_STATE,
59     DEVICE_CMD_GET_CTX_FROM_QUEUE,
60     DEVICE_CMD_GET_DATA_FROM_QUEUE,
61     DEVICE_CMD_RELEASE_CONTEXT,
62     DEVICE_CMD_GET_ELEMENT,
63     DEVICE_CMD_GET_CONTEXT_INDEX,
64     DEVICE_CMD_GET_DEVICE_INFO,
65     DEVICE_CMD_GET_PROFILE_STATUS,
66 };
67
68 enum thread_state {
69     CODEC_TASK_START    = 0,
70     CODEC_TASK_END      = 0x1f,
71 };
72
73 static void brillcodec_threads_create(MaruBrillCodecState *s)
74 {
75     int index;
76     QemuThread *pthread = NULL;
77
78     TRACE("enter: %s\n", __func__);
79
80     pthread = g_malloc(sizeof(QemuThread) * s->worker_thread_cnt);
81     if (!pthread) {
82         ERR("failed to allocate threadpool memory.\n");
83         return;
84     }
85
86     qemu_cond_init(&s->threadpool.cond);
87     qemu_mutex_init(&s->threadpool.mutex);
88
89     s->is_thread_running = true;
90
91     qemu_mutex_lock(&s->context_mutex);
92     s->idle_thread_cnt = 0;
93     qemu_mutex_unlock(&s->context_mutex);
94
95     for (index = 0; index < s->worker_thread_cnt; index++) {
96         qemu_thread_create(&pthread[index], CODEC_DEVICE_THREAD,
97             brillcodec_threads, (void *)s, QEMU_THREAD_JOINABLE);
98     }
99
100     s->threadpool.threads = pthread;
101
102     TRACE("leave: %s\n", __func__);
103 }
104
105 static void brillcodec_get_cpu_cores(MaruBrillCodecState *s)
106 {
107     s->worker_thread_cnt = get_number_of_processors();
108     if (s->worker_thread_cnt < DEFAULT_WORKER_THREAD_CNT) {
109         s->worker_thread_cnt = DEFAULT_WORKER_THREAD_CNT;
110     }
111
112     TRACE("number of threads: %d\n", s->worker_thread_cnt);
113 }
114
115 static void brillcodec_bh_callback(void *opaque)
116 {
117     MaruBrillCodecState *s = (MaruBrillCodecState *)opaque;
118
119     TRACE("enter: %s\n", __func__);
120
121     qemu_mutex_lock(&s->context_queue_mutex);
122     if (!QTAILQ_EMPTY(&codec_wq)) {
123         qemu_mutex_unlock(&s->context_queue_mutex);
124
125         TRACE("raise irq\n");
126         pci_set_irq(&s->dev, 1);
127         s->irq_raised = 1;
128     } else {
129         qemu_mutex_unlock(&s->context_queue_mutex);
130         TRACE("codec_wq is empty!!\n");
131     }
132
133     TRACE("leave: %s\n", __func__);
134 }
135
136 static uint64_t brillcodec_read(void *opaque,
137                                         hwaddr addr,
138                                         unsigned size)
139 {
140     MaruBrillCodecState *s = (MaruBrillCodecState *)opaque;
141     uint64_t ret = 0;
142
143     switch (addr >> 2) {
144     case DEVICE_CMD_GET_THREAD_STATE:
145         qemu_mutex_lock(&s->context_queue_mutex);
146         if (s->irq_raised) {
147             ret = CODEC_TASK_END;
148             pci_set_irq(&s->dev, 0);
149             s->irq_raised = 0;
150         }
151         qemu_mutex_unlock(&s->context_queue_mutex);
152
153         TRACE("get thread_state. ret: %d\n", ret);
154         break;
155
156     case DEVICE_CMD_GET_CTX_FROM_QUEUE:
157     {
158         DeviceMemEntry *head = NULL;
159
160         qemu_mutex_lock(&s->context_queue_mutex);
161         head = QTAILQ_FIRST(&codec_wq);
162         if (head) {
163             ret = head->ctx_id;
164             QTAILQ_REMOVE(&codec_wq, head, node);
165             entry[ret] = head;
166             TRACE("get a elem from codec_wq. 0x%x\n", head);
167         } else {
168             ret = 0;
169         }
170         qemu_mutex_unlock(&s->context_queue_mutex);
171
172         TRACE("get a head from a writequeue. head: %x\n", ret);
173     }
174         break;
175
176     case DEVICE_CMD_GET_DEVICE_INFO:
177         ret |= CODEC_MAJOR_VERSION << 8;
178         ret |= CODEC_MINOR_VERSION;
179         TRACE("codec version: %d.%d.%d\n", CODEC_MAJOR_VERSION, CODEC_MINOR_VERSION, 0);
180
181         ret |= s->memory_monopolizing << 16;
182         break;
183
184     case DEVICE_CMD_GET_ELEMENT:
185         ret = brillcodec_query_list(s);
186         break;
187
188     case DEVICE_CMD_GET_CONTEXT_INDEX:
189         ret = brillcodec_get_context_index(s);
190         TRACE("get context index: %d\n", ret);
191         break;
192
193     case DEVICE_CMD_GET_PROFILE_STATUS:
194         ret = s->profile;
195         TRACE("profile status: %d\n", s->profile);
196         break;
197
198     default:
199         ERR("no avaiable command for read. %d\n", addr);
200     }
201
202     return ret;
203 }
204
205 static void brillcodec_write(void *opaque, hwaddr addr,
206                                     uint64_t value, unsigned size)
207 {
208     MaruBrillCodecState *s = (MaruBrillCodecState *)opaque;
209
210     switch (addr >> 2) {
211     case DEVICE_CMD_API_INDEX:
212         TRACE("set codec_cmd value: %d\n", value);
213         s->ioparam.api_index = value;
214         brillcodec_wakeup_threads(s, value);
215         break;
216
217     case DEVICE_CMD_CONTEXT_INDEX:
218         TRACE("set context_index value: %d\n", value);
219         s->ioparam.ctx_index = value;
220         break;
221
222     case DEVICE_CMD_DEVICE_MEM_OFFSET:
223         TRACE("set mem_offset value: 0x%x\n", value);
224         s->ioparam.mem_offset = value;
225         break;
226
227     case DEVICE_CMD_RELEASE_CONTEXT:
228     {
229         int ctx_id = (int32_t)value;
230
231         if (CONTEXT(s, ctx_id)->occupied_thread) {
232             CONTEXT(s, ctx_id)->requested_close = true;
233             INFO("make running thread to handle deinit\n");
234         } else {
235             brillcodec_release_context(s, ctx_id);
236         }
237     }
238         break;
239
240     case DEVICE_CMD_GET_DATA_FROM_QUEUE:
241         brillcodec_pop_writequeue(s, (uint32_t)value);
242         break;
243
244     default:
245         ERR("no available command for write. %d\n", addr);
246     }
247 }
248
249 static const MemoryRegionOps brillcodec_mmio_ops = {
250     .read = brillcodec_read,
251     .write = brillcodec_write,
252     .valid = {
253         .min_access_size = 4,
254         .max_access_size = 4,
255         .unaligned = false
256     },
257     .endianness = DEVICE_LITTLE_ENDIAN,
258 };
259
260 static int brillcodec_initfn(PCIDevice *dev)
261 {
262     MaruBrillCodecState *s = DO_UPCAST(MaruBrillCodecState, dev, dev);
263     uint8_t *pci_conf = s->dev.config;
264
265     INFO("device initialization.\n");
266     INFO("version: %d.%d.%d\n", CODEC_MAJOR_VERSION, CODEC_MINOR_VERSION, 0);
267
268     pci_config_set_interrupt_pin(pci_conf, 1);
269
270     memory_region_init_ram(&s->vram, OBJECT(s), "maru_brill_codec.vram", CODEC_MEM_SIZE, &error_abort);
271     s->vaddr = (uint8_t *)memory_region_get_ram_ptr(&s->vram);
272
273     memory_region_init_io(&s->mmio, OBJECT(s), &brillcodec_mmio_ops, s,
274                         "maru_brill_codec.mmio", CODEC_REG_SIZE);
275
276     pci_register_bar(&s->dev, 0, PCI_BASE_ADDRESS_MEM_PREFETCH, &s->vram);
277     pci_register_bar(&s->dev, 1, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mmio);
278
279     qemu_mutex_init(&s->context_mutex);
280     qemu_mutex_init(&s->context_queue_mutex);
281     qemu_mutex_init(&s->ioparam_queue_mutex);
282
283     brillcodec_get_cpu_cores(s);
284     brillcodec_threads_create(s);
285
286     // register a function to qemu bottom-halves to switch context.
287     s->codec_bh = qemu_bh_new(brillcodec_bh_callback, s);
288
289     // register plugins
290     if ((s->hwaccel_plugin = probe_plugin())) {
291         INFO("%s extension is enabled.\n", s->hwaccel_plugin->name);
292     }
293
294     // configurations
295     s->memory_monopolizing = 0;
296 #ifdef SUPPORT_MEMORY_MONOPOLIZING
297 # ifdef ENCODE_VIDEO_USE_MEMORY_MONOPOLIZING
298     s->memory_monopolizing |= 1 << ENCODE_VIDEO;
299     INFO("API [%d] use memory monopolizing.\n", ENCODE_VIDEO);
300 # endif
301 #endif
302
303     if (s->profile) {
304         INFO("Profile the brillcodec.(%d)\n", s->profile);
305     }
306
307     return 0;
308 }
309
310 static void brillcodec_exitfn(PCIDevice *dev)
311 {
312     MaruBrillCodecState *s = DO_UPCAST(MaruBrillCodecState, dev, dev);
313     INFO("device exit\n");
314
315     qemu_mutex_destroy(&s->context_mutex);
316     qemu_mutex_destroy(&s->context_queue_mutex);
317     qemu_mutex_destroy(&s->ioparam_queue_mutex);
318
319     qemu_bh_delete(s->codec_bh);
320 }
321
322 static void brillcodec_reset(DeviceState *d)
323 {
324     MaruBrillCodecState *s = (MaruBrillCodecState *)d;
325     INFO("device reset\n");
326
327     s->irq_raised = 0;
328
329     memset(&s->context, 0, sizeof(CodecContext) * CODEC_CONTEXT_MAX);
330     memset(&s->ioparam, 0, sizeof(CodecParam));
331 }
332
333 static Property brillcodec_props[] = {
334     DEFINE_PROP_UINT8("profile", MaruBrillCodecState, profile, 0),
335     DEFINE_PROP_END_OF_LIST(),
336 };
337
338 static void brillcodec_class_init(ObjectClass *klass, void *data)
339 {
340     DeviceClass *dc = DEVICE_CLASS(klass);
341     PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
342
343     k->init = brillcodec_initfn;
344     k->exit = brillcodec_exitfn;
345     k->vendor_id = PCI_VENDOR_ID_TIZEN;
346     k->device_id = PCI_DEVICE_ID_VIRTUAL_BRILL_CODEC;
347     k->class_id = PCI_CLASS_OTHERS;
348     dc->reset = brillcodec_reset;
349     dc->props = brillcodec_props;
350     dc->desc = "Virtual new codec device for Tizen emulator";
351 }
352
353 static TypeInfo codec_device_info = {
354     .name          = CODEC_DEVICE_NAME,
355     .parent        = TYPE_PCI_DEVICE,
356     .instance_size = sizeof(MaruBrillCodecState),
357     .class_init    = brillcodec_class_init,
358 };
359
360 static void codec_register_types(void)
361 {
362     type_register_static(&codec_device_info);
363 }
364
365 type_init(codec_register_types)