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/osdep.h"
35 #include "qemu/main-loop.h"
36 #include "qapi/error.h"
37 #include "hw/pci/pci.h"
39 #include "hw/maru_device_ids.h"
40 #include "util/osutil.h"
41 #include "maru_brillcodec.h"
42 #include "util/new_debug_ch.h"
44 /* define debug channel */
45 DECLARE_DEBUG_CHANNEL(brillcodec);
47 #define CODEC_DEVICE_NAME "codec-pci"
48 #define CODEC_DEVICE_THREAD "codec-workthread"
50 #define CODEC_MAJOR_VERSION 3
51 #define CODEC_MINOR_VERSION 0
53 #define CODEC_REG_SIZE (256)
54 #define DEFAULT_WORKER_THREAD_CNT 8
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,
72 CODEC_TASK_END = 0x1f,
75 static void brillcodec_threads_create(MaruBrillCodecState *s)
78 QemuThread *pthread = NULL;
80 LOG_TRACE("enter: %s\n", __func__);
82 pthread = g_malloc(sizeof(QemuThread) * s->worker_thread_cnt);
84 LOG_SEVERE("failed to allocate threadpool memory.\n");
88 qemu_cond_init(&s->threadpool.cond);
89 qemu_mutex_init(&s->threadpool.mutex);
91 s->is_thread_running = true;
93 qemu_mutex_lock(&s->context_mutex);
94 s->idle_thread_cnt = 0;
95 qemu_mutex_unlock(&s->context_mutex);
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);
102 s->threadpool.threads = pthread;
104 LOG_TRACE("leave: %s\n", __func__);
107 static void brillcodec_get_cpu_cores(MaruBrillCodecState *s)
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;
114 LOG_TRACE("number of threads: %d\n", s->worker_thread_cnt);
117 static void brillcodec_bh_callback(void *opaque)
119 MaruBrillCodecState *s = (MaruBrillCodecState *)opaque;
121 LOG_TRACE("enter: %s\n", __func__);
123 qemu_mutex_lock(&s->context_queue_mutex);
124 if (!QTAILQ_EMPTY(&codec_wq)) {
125 qemu_mutex_unlock(&s->context_queue_mutex);
127 LOG_TRACE("raise irq\n");
128 pci_set_irq(&s->dev, 1);
131 qemu_mutex_unlock(&s->context_queue_mutex);
132 LOG_TRACE("codec_wq is empty!!\n");
135 LOG_TRACE("leave: %s\n", __func__);
138 static uint64_t brillcodec_read(void *opaque,
142 MaruBrillCodecState *s = (MaruBrillCodecState *)opaque;
146 case DEVICE_CMD_GET_THREAD_STATE:
147 qemu_mutex_lock(&s->context_queue_mutex);
149 ret = CODEC_TASK_END;
150 pci_set_irq(&s->dev, 0);
153 qemu_mutex_unlock(&s->context_queue_mutex);
155 LOG_TRACE("get thread_state. ret: %d\n", ret);
158 case DEVICE_CMD_GET_CTX_FROM_QUEUE:
160 DeviceMemEntry *head = NULL;
162 qemu_mutex_lock(&s->context_queue_mutex);
163 head = QTAILQ_FIRST(&codec_wq);
166 QTAILQ_REMOVE(&codec_wq, head, node);
168 LOG_TRACE("get a elem from codec_wq. 0x%x\n", head);
172 qemu_mutex_unlock(&s->context_queue_mutex);
174 LOG_TRACE("get a head from a writequeue. head: %x\n", ret);
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);
183 ret |= s->memory_monopolizing << 16;
186 case DEVICE_CMD_GET_ELEMENT:
187 ret = brillcodec_query_list(s);
190 case DEVICE_CMD_GET_CONTEXT_INDEX:
191 ret = brillcodec_get_context_index(s);
192 LOG_TRACE("get context index: %d\n", ret);
195 case DEVICE_CMD_GET_PROFILE_STATUS:
197 LOG_TRACE("profile status: %d\n", s->profile);
201 LOG_SEVERE("no avaiable command for read. %d\n", addr);
207 static void brillcodec_write(void *opaque, hwaddr addr,
208 uint64_t value, unsigned size)
210 MaruBrillCodecState *s = (MaruBrillCodecState *)opaque;
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);
219 case DEVICE_CMD_CONTEXT_INDEX:
220 LOG_TRACE("set context_index value: %d\n", value);
221 s->ioparam.ctx_index = value;
224 case DEVICE_CMD_DEVICE_MEM_OFFSET:
225 LOG_TRACE("set mem_offset value: 0x%x\n", value);
226 s->ioparam.mem_offset = value;
229 case DEVICE_CMD_RELEASE_CONTEXT:
231 int ctx_id = (int32_t)value;
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");
237 brillcodec_release_context(s, ctx_id);
242 case DEVICE_CMD_GET_DATA_FROM_QUEUE:
243 brillcodec_pop_writequeue(s, (uint32_t)value);
247 LOG_SEVERE("no available command for write. %d\n", addr);
251 static const MemoryRegionOps brillcodec_mmio_ops = {
252 .read = brillcodec_read,
253 .write = brillcodec_write,
255 .min_access_size = 4,
256 .max_access_size = 4,
259 .endianness = DEVICE_LITTLE_ENDIAN,
262 static int brillcodec_initfn(PCIDevice *dev)
264 MaruBrillCodecState *s = DO_UPCAST(MaruBrillCodecState, dev, dev);
265 uint8_t *pci_conf = s->dev.config;
267 LOG_INFO("device initialization.\n");
268 LOG_INFO("version: %d.%d.%d\n", CODEC_MAJOR_VERSION, CODEC_MINOR_VERSION, 0);
270 pci_config_set_interrupt_pin(pci_conf, 1);
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);
275 memory_region_init_io(&s->mmio, OBJECT(s), &brillcodec_mmio_ops, s,
276 "maru_brill_codec.mmio", CODEC_REG_SIZE);
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);
281 qemu_mutex_init(&s->context_mutex);
282 qemu_mutex_init(&s->context_queue_mutex);
283 qemu_mutex_init(&s->ioparam_queue_mutex);
285 brillcodec_get_cpu_cores(s);
286 brillcodec_threads_create(s);
288 // register a function to qemu bottom-halves to switch context.
289 s->codec_bh = qemu_bh_new(brillcodec_bh_callback, s);
292 if ((s->hwaccel_plugin = probe_plugin())) {
293 LOG_INFO("%s extension is enabled.\n", s->hwaccel_plugin->name);
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);
306 LOG_INFO("Profile the brillcodec.(%d)\n", s->profile);
312 static void brillcodec_exitfn(PCIDevice *dev)
314 MaruBrillCodecState *s = DO_UPCAST(MaruBrillCodecState, dev, dev);
315 LOG_INFO("device exit\n");
317 qemu_mutex_destroy(&s->context_mutex);
318 qemu_mutex_destroy(&s->context_queue_mutex);
319 qemu_mutex_destroy(&s->ioparam_queue_mutex);
321 qemu_bh_delete(s->codec_bh);
324 static void brillcodec_reset(DeviceState *d)
326 MaruBrillCodecState *s = (MaruBrillCodecState *)d;
327 LOG_INFO("device reset\n");
331 memset(&s->context, 0, sizeof(CodecContext) * CODEC_CONTEXT_MAX);
332 memset(&s->ioparam, 0, sizeof(CodecParam));
335 static Property brillcodec_props[] = {
336 DEFINE_PROP_UINT8("profile", MaruBrillCodecState, profile, 0),
337 DEFINE_PROP_END_OF_LIST(),
340 static void brillcodec_class_init(ObjectClass *klass, void *data)
342 DeviceClass *dc = DEVICE_CLASS(klass);
343 PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
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";
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,
362 static void codec_register_types(void)
364 type_register_static(&codec_device_info);
367 type_init(codec_register_types)