4 * Copyright (c) 2013 - 2014 Samsung Electronics Co., Ltd All Rights Reserved
7 * Kitae Kim <kt920.kim@samsung.com>
8 * SeokYeon Hwang <syeon.hwang@samsung.com>
9 * YeongKyoon Lee <yeongkyoon.lee@samsung.com>
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.
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.
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,
31 #define SUPPORT_MEMORY_MONOPOLIZING
32 //#define ENCODE_VIDEO_USE_MEMORY_MONOPOLIZING
34 #include "qemu/main-loop.h"
35 #include "hw/pci/pci.h"
37 #include "hw/maru_device_ids.h"
38 #include "util/osutil.h"
39 #include "maru_brillcodec.h"
42 /* define debug channel */
43 MULTI_DEBUG_CHANNEL(qemu, brillcodec);
45 #define CODEC_DEVICE_NAME "codec-pci"
46 #define CODEC_DEVICE_THREAD "codec-workthread"
48 #define CODEC_MAJOR_VERSION 3
49 #define CODEC_MINOR_VERSION 0
51 #define CODEC_REG_SIZE (256)
52 #define DEFAULT_WORKER_THREAD_CNT 8
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,
70 CODEC_TASK_END = 0x1f,
73 static void brillcodec_threads_create(MaruBrillCodecState *s)
76 QemuThread *pthread = NULL;
78 TRACE("enter: %s\n", __func__);
80 pthread = g_malloc(sizeof(QemuThread) * s->worker_thread_cnt);
82 ERR("failed to allocate threadpool memory.\n");
86 qemu_cond_init(&s->threadpool.cond);
87 qemu_mutex_init(&s->threadpool.mutex);
89 s->is_thread_running = true;
91 qemu_mutex_lock(&s->context_mutex);
92 s->idle_thread_cnt = 0;
93 qemu_mutex_unlock(&s->context_mutex);
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);
100 s->threadpool.threads = pthread;
102 TRACE("leave: %s\n", __func__);
105 static void brillcodec_get_cpu_cores(MaruBrillCodecState *s)
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;
112 TRACE("number of threads: %d\n", s->worker_thread_cnt);
115 static void brillcodec_bh_callback(void *opaque)
117 MaruBrillCodecState *s = (MaruBrillCodecState *)opaque;
119 TRACE("enter: %s\n", __func__);
121 qemu_mutex_lock(&s->context_queue_mutex);
122 if (!QTAILQ_EMPTY(&codec_wq)) {
123 qemu_mutex_unlock(&s->context_queue_mutex);
125 TRACE("raise irq\n");
126 pci_set_irq(&s->dev, 1);
129 qemu_mutex_unlock(&s->context_queue_mutex);
130 TRACE("codec_wq is empty!!\n");
133 TRACE("leave: %s\n", __func__);
136 static uint64_t brillcodec_read(void *opaque,
140 MaruBrillCodecState *s = (MaruBrillCodecState *)opaque;
144 case DEVICE_CMD_GET_THREAD_STATE:
145 qemu_mutex_lock(&s->context_queue_mutex);
147 ret = CODEC_TASK_END;
148 pci_set_irq(&s->dev, 0);
151 qemu_mutex_unlock(&s->context_queue_mutex);
153 TRACE("get thread_state. ret: %d\n", ret);
156 case DEVICE_CMD_GET_CTX_FROM_QUEUE:
158 DeviceMemEntry *head = NULL;
160 qemu_mutex_lock(&s->context_queue_mutex);
161 head = QTAILQ_FIRST(&codec_wq);
164 QTAILQ_REMOVE(&codec_wq, head, node);
166 TRACE("get a elem from codec_wq. 0x%x\n", head);
170 qemu_mutex_unlock(&s->context_queue_mutex);
172 TRACE("get a head from a writequeue. head: %x\n", ret);
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);
181 ret |= s->memory_monopolizing << 16;
184 case DEVICE_CMD_GET_ELEMENT:
185 ret = brillcodec_query_list(s);
188 case DEVICE_CMD_GET_CONTEXT_INDEX:
189 ret = brillcodec_get_context_index(s);
190 TRACE("get context index: %d\n", ret);
193 case DEVICE_CMD_GET_PROFILE_STATUS:
195 TRACE("profile status: %d\n", s->profile);
199 ERR("no avaiable command for read. %d\n", addr);
205 static void brillcodec_write(void *opaque, hwaddr addr,
206 uint64_t value, unsigned size)
208 MaruBrillCodecState *s = (MaruBrillCodecState *)opaque;
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);
217 case DEVICE_CMD_CONTEXT_INDEX:
218 TRACE("set context_index value: %d\n", value);
219 s->ioparam.ctx_index = value;
222 case DEVICE_CMD_DEVICE_MEM_OFFSET:
223 TRACE("set mem_offset value: 0x%x\n", value);
224 s->ioparam.mem_offset = value;
227 case DEVICE_CMD_RELEASE_CONTEXT:
229 int ctx_id = (int32_t)value;
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");
235 brillcodec_release_context(s, ctx_id);
240 case DEVICE_CMD_GET_DATA_FROM_QUEUE:
241 brillcodec_pop_writequeue(s, (uint32_t)value);
245 ERR("no available command for write. %d\n", addr);
249 static const MemoryRegionOps brillcodec_mmio_ops = {
250 .read = brillcodec_read,
251 .write = brillcodec_write,
253 .min_access_size = 4,
254 .max_access_size = 4,
257 .endianness = DEVICE_LITTLE_ENDIAN,
260 static int brillcodec_initfn(PCIDevice *dev)
262 MaruBrillCodecState *s = DO_UPCAST(MaruBrillCodecState, dev, dev);
263 uint8_t *pci_conf = s->dev.config;
265 INFO("device initialization.\n");
266 INFO("version: %d.%d.%d\n", CODEC_MAJOR_VERSION, CODEC_MINOR_VERSION, 0);
268 pci_config_set_interrupt_pin(pci_conf, 1);
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);
273 memory_region_init_io(&s->mmio, OBJECT(s), &brillcodec_mmio_ops, s,
274 "maru_brill_codec.mmio", CODEC_REG_SIZE);
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);
279 qemu_mutex_init(&s->context_mutex);
280 qemu_mutex_init(&s->context_queue_mutex);
281 qemu_mutex_init(&s->ioparam_queue_mutex);
283 brillcodec_get_cpu_cores(s);
284 brillcodec_threads_create(s);
286 // register a function to qemu bottom-halves to switch context.
287 s->codec_bh = qemu_bh_new(brillcodec_bh_callback, s);
290 if ((s->hwaccel_plugin = probe_plugin())) {
291 INFO("%s extension is enabled.\n", s->hwaccel_plugin->name);
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);
304 INFO("Profile the brillcodec.(%d)\n", s->profile);
310 static void brillcodec_exitfn(PCIDevice *dev)
312 MaruBrillCodecState *s = DO_UPCAST(MaruBrillCodecState, dev, dev);
313 INFO("device exit\n");
315 qemu_mutex_destroy(&s->context_mutex);
316 qemu_mutex_destroy(&s->context_queue_mutex);
317 qemu_mutex_destroy(&s->ioparam_queue_mutex);
319 qemu_bh_delete(s->codec_bh);
322 static void brillcodec_reset(DeviceState *d)
324 MaruBrillCodecState *s = (MaruBrillCodecState *)d;
325 INFO("device reset\n");
329 memset(&s->context, 0, sizeof(CodecContext) * CODEC_CONTEXT_MAX);
330 memset(&s->ioparam, 0, sizeof(CodecParam));
333 static Property brillcodec_props[] = {
334 DEFINE_PROP_UINT8("profile", MaruBrillCodecState, profile, 0),
335 DEFINE_PROP_END_OF_LIST(),
338 static void brillcodec_class_init(ObjectClass *klass, void *data)
340 DeviceClass *dc = DEVICE_CLASS(klass);
341 PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
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";
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,
360 static void codec_register_types(void)
362 type_register_static(&codec_device_info);
365 type_init(codec_register_types)