#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/completion.h>
#include <linux/vmalloc.h>
#include <media/videobuf2-vmalloc.h>
#include "../include/linux/raspberrypi/vchiq.h"
#include "mmal-common.h"
#include "mmal-vchiq.h"
#include "mmal-msg.h"
#define VCHIQ_MMAL_MAX_COMPONENTS 64
#define SYNC_MSG_TIMEOUT 3
#ifdef DEBUG
static const char *const msg_type_names[] = {
"UNKNOWN",
"QUIT",
"SERVICE_CLOSED",
"GET_VERSION",
"COMPONENT_CREATE",
"COMPONENT_DESTROY",
"COMPONENT_ENABLE",
"COMPONENT_DISABLE",
"PORT_INFO_GET",
"PORT_INFO_SET",
"PORT_ACTION",
"BUFFER_FROM_HOST",
"BUFFER_TO_HOST",
"GET_STATS",
"PORT_PARAMETER_SET",
"PORT_PARAMETER_GET",
"EVENT_TO_HOST",
"GET_CORE_STATS_FOR_PORT",
"OPAQUE_ALLOCATOR",
"CONSUME_MEM",
"LMK",
"OPAQUE_ALLOCATOR_DESC",
"DRM_GET_LHS32",
"DRM_GET_TIME",
"BUFFER_FROM_HOST_ZEROLEN",
"PORT_FLUSH",
"HOST_LOG",
};
#endif
static const char *const port_action_type_names[] = {
"UNKNOWN",
"ENABLE",
"DISABLE",
"FLUSH",
"CONNECT",
"DISCONNECT",
"SET_REQUIREMENTS",
};
#if defined(DEBUG)
#if defined(FULL_MSG_DUMP)
#define DBG_DUMP_MSG(MSG, MSG_LEN, TITLE) \
do { \
pr_debug(TITLE" type:%s(%d) length:%d\n", \
msg_type_names[(MSG)->h.type], \
(MSG)->h.type, (MSG_LEN)); \
print_hex_dump(KERN_DEBUG, "<<h: ", DUMP_PREFIX_OFFSET, \
16, 4, (MSG), \
sizeof(struct mmal_msg_header), 1); \
print_hex_dump(KERN_DEBUG, "<<p: ", DUMP_PREFIX_OFFSET, \
16, 4, \
((u8 *)(MSG)) + sizeof(struct mmal_msg_header),\
(MSG_LEN) - sizeof(struct mmal_msg_header), 1); \
} while (0)
#else
#define DBG_DUMP_MSG(MSG, MSG_LEN, TITLE) \
{ \
pr_debug(TITLE" type:%s(%d) length:%d\n", \
msg_type_names[(MSG)->h.type], \
(MSG)->h.type, (MSG_LEN)); \
}
#endif
#else
#define DBG_DUMP_MSG(MSG, MSG_LEN, TITLE)
#endif
struct vchiq_mmal_instance;
struct mmal_msg_context {
struct vchiq_mmal_instance *instance;
int handle;
union {
struct {
struct work_struct work;
struct work_struct buffer_to_host_work;
struct vchiq_mmal_instance *instance;
struct vchiq_mmal_port *port;
struct mmal_buffer *buffer;
unsigned long buffer_used;
u32 mmal_flags;
s64 pts;
s64 dts;
int status;
} bulk;
struct {
struct vchiq_header *msg_handle;
struct mmal_msg *msg;
u32 msg_len;
struct completion cmplt;
} sync;
} u;
};
struct vchiq_mmal_instance {
unsigned int service_handle;
struct mutex vchiq_mutex;
struct idr context_map;
struct mutex context_map_lock;
struct vchiq_mmal_component component[VCHIQ_MMAL_MAX_COMPONENTS];
struct workqueue_struct *bulk_wq;
struct vchiq_instance *vchiq_instance;
};
static struct mmal_msg_context *
get_msg_context(struct vchiq_mmal_instance *instance)
{
struct mmal_msg_context *msg_context;
int handle;
msg_context = kzalloc(sizeof(*msg_context), GFP_KERNEL);
if (!msg_context)
return ERR_PTR(-ENOMEM);
mutex_lock(&instance->context_map_lock);
handle = idr_alloc(&instance->context_map, msg_context,
0, 0, GFP_KERNEL);
mutex_unlock(&instance->context_map_lock);
if (handle < 0) {
kfree(msg_context);
return ERR_PTR(handle);
}
msg_context->instance = instance;
msg_context->handle = handle;
return msg_context;
}
static struct mmal_msg_context *
lookup_msg_context(struct vchiq_mmal_instance *instance, int handle)
{
return idr_find(&instance->context_map, handle);
}
static void
release_msg_context(struct mmal_msg_context *msg_context)
{
struct vchiq_mmal_instance *instance = msg_context->instance;
mutex_lock(&instance->context_map_lock);
idr_remove(&instance->context_map, msg_context->handle);
mutex_unlock(&instance->context_map_lock);
kfree(msg_context);
}
static void event_to_host_cb(struct vchiq_mmal_instance *instance,
struct mmal_msg *msg, u32 msg_len)
{
pr_debug("unhandled event\n");
pr_debug("component:%u port type:%d num:%d cmd:0x%x length:%d\n",
msg->u.event_to_host.client_component,
msg->u.event_to_host.port_type,
msg->u.event_to_host.port_num,
msg->u.event_to_host.cmd, msg->u.event_to_host.length);
}
static void buffer_work_cb(struct work_struct *work)
{
struct mmal_msg_context *msg_context =
container_of(work, struct mmal_msg_context, u.bulk.work);
struct mmal_buffer *buffer = msg_context->u.bulk.buffer;
if (!buffer) {
pr_err("%s: ctx: %p, No mmal buffer to pass details\n",
__func__, msg_context);
return;
}
buffer->length = msg_context->u.bulk.buffer_used;
buffer->mmal_flags = msg_context->u.bulk.mmal_flags;
buffer->dts = msg_context->u.bulk.dts;
buffer->pts = msg_context->u.bulk.pts;
atomic_dec(&msg_context->u.bulk.port->buffers_with_vpu);
msg_context->u.bulk.port->buffer_cb(msg_context->u.bulk.instance,
msg_context->u.bulk.port,
msg_context->u.bulk.status,
msg_context->u.bulk.buffer);
}
static void buffer_to_host_work_cb(struct work_struct *work)
{
struct mmal_msg_context *msg_context =
container_of(work, struct mmal_msg_context,
u.bulk.buffer_to_host_work);
struct vchiq_mmal_instance *instance = msg_context->instance;
unsigned long len = msg_context->u.bulk.buffer_used;
int ret;
if (!len)
len = 8;
vchiq_use_service(instance->vchiq_instance, instance->service_handle);
ret = vchiq_bulk_receive(instance->vchiq_instance, instance->service_handle,
msg_context->u.bulk.buffer->buffer,
(len + 3) & ~3,
msg_context,
VCHIQ_BULK_MODE_CALLBACK);
vchiq_release_service(instance->vchiq_instance, instance->service_handle);
if (ret != 0)
pr_err("%s: ctx: %p, vchiq_bulk_receive failed %d\n",
__func__, msg_context, ret);
}
static int bulk_receive(struct vchiq_mmal_instance *instance,
struct mmal_msg *msg,
struct mmal_msg_context *msg_context)
{
unsigned long rd_len;
rd_len = msg->u.buffer_from_host.buffer_header.length;
if (!msg_context->u.bulk.buffer) {
pr_err("bulk.buffer not configured - error in buffer_from_host\n");
return -EINVAL;
}
if (rd_len > msg_context->u.bulk.buffer->buffer_size) {
rd_len = msg_context->u.bulk.buffer->buffer_size;
pr_warn("short read as not enough receive buffer space\n");
}
msg_context->u.bulk.buffer_used = rd_len;
msg_context->u.bulk.dts = msg->u.buffer_from_host.buffer_header.dts;
msg_context->u.bulk.pts = msg->u.buffer_from_host.buffer_header.pts;
queue_work(msg_context->instance->bulk_wq,
&msg_context->u.bulk.buffer_to_host_work);
return 0;
}
static int inline_receive(struct vchiq_mmal_instance *instance,
struct mmal_msg *msg,
struct mmal_msg_context *msg_context)
{
memcpy(msg_context->u.bulk.buffer->buffer,
msg->u.buffer_from_host.short_data,
msg->u.buffer_from_host.payload_in_message);
msg_context->u.bulk.buffer_used =
msg->u.buffer_from_host.payload_in_message;
return 0;
}
static int
buffer_from_host(struct vchiq_mmal_instance *instance,
struct vchiq_mmal_port *port, struct mmal_buffer *buf)
{
struct mmal_msg_context *msg_context;
struct mmal_msg m;
int ret;
if (!port->enabled)
return -EINVAL;
pr_debug("instance:%u buffer:%p\n", instance->service_handle, buf);
if (!buf->msg_context) {
pr_err("%s: msg_context not allocated, buf %p\n", __func__,
buf);
return -EINVAL;
}
msg_context = buf->msg_context;
msg_context->u.bulk.instance = instance;
msg_context->u.bulk.port = port;
msg_context->u.bulk.buffer = buf;
msg_context->u.bulk.buffer_used = 0;
INIT_WORK(&msg_context->u.bulk.work, buffer_work_cb);
INIT_WORK(&msg_context->u.bulk.buffer_to_host_work,
buffer_to_host_work_cb);
atomic_inc(&port->buffers_with_vpu);
memset(&m, 0xbc, sizeof(m));
m.h.type = MMAL_MSG_TYPE_BUFFER_FROM_HOST;
m.h.magic = MMAL_MAGIC;
m.h.context = msg_context->handle;
m.h.status = 0;
m.u.buffer_from_host.drvbuf.magic = MMAL_MAGIC;
m.u.buffer_from_host.drvbuf.component_handle = port->component->handle;
m.u.buffer_from_host.drvbuf.port_handle = port->handle;
m.u.buffer_from_host.drvbuf.client_context = msg_context->handle;
m.u.buffer_from_host.buffer_header.cmd = 0;
m.u.buffer_from_host.buffer_header.data =
(u32)(unsigned long)buf->buffer;
m.u.buffer_from_host.buffer_header.alloc_size = buf->buffer_size;
m.u.buffer_from_host.buffer_header.length = 0;
m.u.buffer_from_host.buffer_header.offset = 0;
m.u.buffer_from_host.buffer_header.flags = 0;
m.u.buffer_from_host.buffer_header.pts = MMAL_TIME_UNKNOWN;
m.u.buffer_from_host.buffer_header.dts = MMAL_TIME_UNKNOWN;
memset(&m.u.buffer_from_host.buffer_header_type_specific, 0,
sizeof(m.u.buffer_from_host.buffer_header_type_specific));
m.u.buffer_from_host.payload_in_message = 0;
vchiq_use_service(instance->vchiq_instance, instance->service_handle);
ret = vchiq_queue_kernel_message(instance->vchiq_instance, instance->service_handle, &m,
sizeof(struct mmal_msg_header) +
sizeof(m.u.buffer_from_host));
if (ret)
atomic_dec(&port->buffers_with_vpu);
vchiq_release_service(instance->vchiq_instance, instance->service_handle);
return ret;
}
static void buffer_to_host_cb(struct vchiq_mmal_instance *instance,
struct mmal_msg *msg, u32 msg_len)
{
struct mmal_msg_context *msg_context;
u32 handle;
pr_debug("%s: instance:%p msg:%p msg_len:%d\n",
__func__, instance, msg, msg_len);
if (msg->u.buffer_from_host.drvbuf.magic == MMAL_MAGIC) {
handle = msg->u.buffer_from_host.drvbuf.client_context;
msg_context = lookup_msg_context(instance, handle);
if (!msg_context) {
pr_err("drvbuf.client_context(%u) is invalid\n",
handle);
return;
}
} else {
pr_err("MMAL_MSG_TYPE_BUFFER_TO_HOST with bad magic\n");
return;
}
msg_context->u.bulk.mmal_flags =
msg->u.buffer_from_host.buffer_header.flags;
if (msg->h.status != MMAL_MSG_STATUS_SUCCESS) {
pr_warn("error %d in reply\n", msg->h.status);
msg_context->u.bulk.status = msg->h.status;
} else if (msg->u.buffer_from_host.buffer_header.length == 0) {
if (msg->u.buffer_from_host.buffer_header.flags &
MMAL_BUFFER_HEADER_FLAG_EOS) {
msg_context->u.bulk.status =
bulk_receive(instance, msg, msg_context);
if (msg_context->u.bulk.status == 0)
return;
} else {
msg_context->u.bulk.status = 0;
msg_context->u.bulk.buffer_used = 0;
}
} else if (msg->u.buffer_from_host.payload_in_message == 0) {
msg_context->u.bulk.status =
bulk_receive(instance, msg, msg_context);
if (msg_context->u.bulk.status == 0)
return;
pr_err("error %d on bulk submission\n",
msg_context->u.bulk.status);
} else if (msg->u.buffer_from_host.payload_in_message <=
MMAL_VC_SHORT_DATA) {
msg_context->u.bulk.status = inline_receive(instance, msg,
msg_context);
} else {
pr_err("message with invalid short payload\n");
msg_context->u.bulk.status = -EINVAL;
msg_context->u.bulk.buffer_used =
msg->u.buffer_from_host.payload_in_message;
}
schedule_work(&msg_context->u.bulk.work);
}
static void bulk_receive_cb(struct vchiq_mmal_instance *instance,
struct mmal_msg_context *msg_context)
{
msg_context->u.bulk.status = 0;
schedule_work(&msg_context->u.bulk.work);
}
static void bulk_abort_cb(struct vchiq_mmal_instance *instance,
struct mmal_msg_context *msg_context)
{
pr_err("%s: bulk ABORTED msg_context:%p\n", __func__, msg_context);
msg_context->u.bulk.status = -EINTR;
schedule_work(&msg_context->u.bulk.work);
}
static int service_callback(struct vchiq_instance *vchiq_instance,
enum vchiq_reason reason, struct vchiq_header *header,
unsigned int handle, void *bulk_ctx)
{
struct vchiq_mmal_instance *instance = vchiq_get_service_userdata(vchiq_instance, handle);
u32 msg_len;
struct mmal_msg *msg;
struct mmal_msg_context *msg_context;
if (!instance) {
pr_err("Message callback passed NULL instance\n");
return 0;
}
switch (reason) {
case VCHIQ_MESSAGE_AVAILABLE:
msg = (void *)header->data;
msg_len = header->size;
DBG_DUMP_MSG(msg, msg_len, "<<< reply message");
switch (msg->h.type) {
case MMAL_MSG_TYPE_BUFFER_FROM_HOST:
vchiq_release_message(vchiq_instance, handle, header);
break;
case MMAL_MSG_TYPE_EVENT_TO_HOST:
event_to_host_cb(instance, msg, msg_len);
vchiq_release_message(vchiq_instance, handle, header);
break;
case MMAL_MSG_TYPE_BUFFER_TO_HOST:
buffer_to_host_cb(instance, msg, msg_len);
vchiq_release_message(vchiq_instance, handle, header);
break;
default:
if (!msg->h.context) {
pr_err("received message context was null!\n");
vchiq_release_message(vchiq_instance, handle, header);
break;
}
msg_context = lookup_msg_context(instance,
msg->h.context);
if (!msg_context) {
pr_err("received invalid message context %u!\n",
msg->h.context);
vchiq_release_message(vchiq_instance, handle, header);
break;
}
msg_context->u.sync.msg_handle = header;
msg_context->u.sync.msg = msg;
msg_context->u.sync.msg_len = msg_len;
complete(&msg_context->u.sync.cmplt);
break;
}
break;
case VCHIQ_BULK_RECEIVE_DONE:
bulk_receive_cb(instance, bulk_ctx);
break;
case VCHIQ_BULK_RECEIVE_ABORTED:
bulk_abort_cb(instance, bulk_ctx);
break;
case VCHIQ_SERVICE_CLOSED:
break;
default:
pr_err("Received unhandled message reason %d\n", reason);
break;
}
return 0;
}
static int send_synchronous_mmal_msg(struct vchiq_mmal_instance *instance,
struct mmal_msg *msg,
unsigned int payload_len,
struct mmal_msg **msg_out,
struct vchiq_header **msg_handle)
{
struct mmal_msg_context *msg_context;
int ret;
unsigned long timeout;
if (payload_len >
(MMAL_MSG_MAX_SIZE - sizeof(struct mmal_msg_header))) {
pr_err("payload length %d exceeds max:%d\n", payload_len,
(int)(MMAL_MSG_MAX_SIZE -
sizeof(struct mmal_msg_header)));
return -EINVAL;
}
msg_context = get_msg_context(instance);
if (IS_ERR(msg_context))
return PTR_ERR(msg_context);
init_completion(&msg_context->u.sync.cmplt);
msg->h.magic = MMAL_MAGIC;
msg->h.context = msg_context->handle;
msg->h.status = 0;
DBG_DUMP_MSG(msg, (sizeof(struct mmal_msg_header) + payload_len),
">>> sync message");
vchiq_use_service(instance->vchiq_instance, instance->service_handle);
ret = vchiq_queue_kernel_message(instance->vchiq_instance, instance->service_handle, msg,
sizeof(struct mmal_msg_header) +
payload_len);
vchiq_release_service(instance->vchiq_instance, instance->service_handle);
if (ret) {
pr_err("error %d queuing message\n", ret);
release_msg_context(msg_context);
return ret;
}
timeout = wait_for_completion_timeout(&msg_context->u.sync.cmplt,
SYNC_MSG_TIMEOUT * HZ);
if (timeout == 0) {
pr_err("timed out waiting for sync completion\n");
ret = -ETIME;
release_msg_context(msg_context);
return ret;
}
*msg_out = msg_context->u.sync.msg;
*msg_handle = msg_context->u.sync.msg_handle;
release_msg_context(msg_context);
return 0;
}
static void dump_port_info(struct vchiq_mmal_port *port)
{
pr_debug("port handle:0x%x enabled:%d\n", port->handle, port->enabled);
pr_debug("buffer minimum num:%d size:%d align:%d\n",
port->minimum_buffer.num,
port->minimum_buffer.size, port->minimum_buffer.alignment);
pr_debug("buffer recommended num:%d size:%d align:%d\n",
port->recommended_buffer.num,
port->recommended_buffer.size,
port->recommended_buffer.alignment);
pr_debug("buffer current values num:%d size:%d align:%d\n",
port->current_buffer.num,
port->current_buffer.size, port->current_buffer.alignment);
pr_debug("elementary stream: type:%d encoding:0x%x variant:0x%x\n",
port->format.type,
port->format.encoding, port->format.encoding_variant);
pr_debug(" bitrate:%d flags:0x%x\n",
port->format.bitrate, port->format.flags);
if (port->format.type == MMAL_ES_TYPE_VIDEO) {
pr_debug
("es video format: width:%d height:%d colourspace:0x%x\n",
port->es.video.width, port->es.video.height,
port->es.video.color_space);
pr_debug(" : crop xywh %d,%d,%d,%d\n",
port->es.video.crop.x,
port->es.video.crop.y,
port->es.video.crop.width, port->es.video.crop.height);
pr_debug(" : framerate %d/%d aspect %d/%d\n",
port->es.video.frame_rate.numerator,
port->es.video.frame_rate.denominator,
port->es.video.par.numerator, port->es.video.par.denominator);
}
}
static void port_to_mmal_msg(struct vchiq_mmal_port *port, struct mmal_port *p)
{
p->type = port->type;
p->index = port->index;
p->index_all = 0;
p->is_enabled = port->enabled;
p->buffer_num_min = port->minimum_buffer.num;
p->buffer_size_min = port->minimum_buffer.size;
p->buffer_alignment_min = port->minimum_buffer.alignment;
p->buffer_num_recommended = port->recommended_buffer.num;
p->buffer_size_recommended = port->recommended_buffer.size;
p->buffer_num = port->current_buffer.num;
p->buffer_size = port->current_buffer.size;
p->userdata = (u32)(unsigned long)port;
}
static int port_info_set(struct vchiq_mmal_instance *instance,
struct vchiq_mmal_port *port)
{
int ret;
struct mmal_msg m;
struct mmal_msg *rmsg;
struct vchiq_header *rmsg_handle;
pr_debug("setting port info port %p\n", port);
if (!port)
return -1;
dump_port_info(port);
m.h.type = MMAL_MSG_TYPE_PORT_INFO_SET;
m.u.port_info_set.component_handle = port->component->handle;
m.u.port_info_set.port_type = port->type;
m.u.port_info_set.port_index = port->index;
port_to_mmal_msg(port, &m.u.port_info_set.port);
m.u.port_info_set.format.type = port->format.type;
m.u.port_info_set.format.encoding = port->format.encoding;
m.u.port_info_set.format.encoding_variant =
port->format.encoding_variant;
m.u.port_info_set.format.bitrate = port->format.bitrate;
m.u.port_info_set.format.flags = port->format.flags;
memcpy(&m.u.port_info_set.es, &port->es,
sizeof(union mmal_es_specific_format));
m.u.port_info_set.format.extradata_size = port->format.extradata_size;
memcpy(&m.u.port_info_set.extradata, port->format.extradata,
port->format.extradata_size);
ret = send_synchronous_mmal_msg(instance, &m,
sizeof(m.u.port_info_set),
&rmsg, &rmsg_handle);
if (ret)
return ret;
if (rmsg->h.type != MMAL_MSG_TYPE_PORT_INFO_SET) {
ret = -EINVAL;
goto release_msg;
}
ret = -rmsg->u.port_info_get_reply.status;
pr_debug("%s:result:%d component:0x%x port:%d\n", __func__, ret,
port->component->handle, port->handle);
release_msg:
vchiq_release_message(instance->vchiq_instance, instance->service_handle, rmsg_handle);
return ret;
}
static int port_info_get(struct vchiq_mmal_instance *instance,
struct vchiq_mmal_port *port)
{
int ret;
struct mmal_msg m;
struct mmal_msg *rmsg;
struct vchiq_header *rmsg_handle;
m.h.type = MMAL_MSG_TYPE_PORT_INFO_GET;
m.u.port_info_get.component_handle = port->component->handle;
m.u.port_info_get.port_type = port->type;
m.u.port_info_get.index = port->index;
ret = send_synchronous_mmal_msg(instance, &m,
sizeof(m.u.port_info_get),
&rmsg, &rmsg_handle);
if (ret)
return ret;
if (rmsg->h.type != MMAL_MSG_TYPE_PORT_INFO_GET) {
ret = -EINVAL;
goto release_msg;
}
ret = -rmsg->u.port_info_get_reply.status;
if (ret != MMAL_MSG_STATUS_SUCCESS)
goto release_msg;
if (rmsg->u.port_info_get_reply.port.is_enabled == 0)
port->enabled = false;
else
port->enabled = true;
port->handle = rmsg->u.port_info_get_reply.port_handle;
port->type = rmsg->u.port_info_get_reply.port_type;
port->index = rmsg->u.port_info_get_reply.port_index;
port->minimum_buffer.num =
rmsg->u.port_info_get_reply.port.buffer_num_min;
port->minimum_buffer.size =
rmsg->u.port_info_get_reply.port.buffer_size_min;
port->minimum_buffer.alignment =
rmsg->u.port_info_get_reply.port.buffer_alignment_min;
port->recommended_buffer.alignment =
rmsg->u.port_info_get_reply.port.buffer_alignment_min;
port->recommended_buffer.num =
rmsg->u.port_info_get_reply.port.buffer_num_recommended;
port->current_buffer.num = rmsg->u.port_info_get_reply.port.buffer_num;
port->current_buffer.size =
rmsg->u.port_info_get_reply.port.buffer_size;
port->format.type = rmsg->u.port_info_get_reply.format.type;
port->format.encoding = rmsg->u.port_info_get_reply.format.encoding;
port->format.encoding_variant =
rmsg->u.port_info_get_reply.format.encoding_variant;
port->format.bitrate = rmsg->u.port_info_get_reply.format.bitrate;
port->format.flags = rmsg->u.port_info_get_reply.format.flags;
memcpy(&port->es,
&rmsg->u.port_info_get_reply.es,
sizeof(union mmal_es_specific_format));
port->format.es = &port->es;
port->format.extradata_size =
rmsg->u.port_info_get_reply.format.extradata_size;
memcpy(port->format.extradata,
rmsg->u.port_info_get_reply.extradata,
port->format.extradata_size);
pr_debug("received port info\n");
dump_port_info(port);
release_msg:
pr_debug("%s:result:%d component:0x%x port:%d\n",
__func__, ret, port->component->handle, port->handle);
vchiq_release_message(instance->vchiq_instance, instance->service_handle, rmsg_handle);
return ret;
}
static int create_component(struct vchiq_mmal_instance *instance,
struct vchiq_mmal_component *component,
const char *name)
{
int ret;
struct mmal_msg m;
struct mmal_msg *rmsg;
struct vchiq_header *rmsg_handle;
m.h.type = MMAL_MSG_TYPE_COMPONENT_CREATE;
m.u.component_create.client_component = component->client_component;
strncpy(m.u.component_create.name, name,
sizeof(m.u.component_create.name));
ret = send_synchronous_mmal_msg(instance, &m,
sizeof(m.u.component_create),
&rmsg, &rmsg_handle);
if (ret)
return ret;
if (rmsg->h.type != m.h.type) {
ret = -EINVAL;
goto release_msg;
}
ret = -rmsg->u.component_create_reply.status;
if (ret != MMAL_MSG_STATUS_SUCCESS)
goto release_msg;
component->handle = rmsg->u.component_create_reply.component_handle;
component->inputs = rmsg->u.component_create_reply.input_num;
component->outputs = rmsg->u.component_create_reply.output_num;
component->clocks = rmsg->u.component_create_reply.clock_num;
pr_debug("Component handle:0x%x in:%d out:%d clock:%d\n",
component->handle,
component->inputs, component->outputs, component->clocks);
release_msg:
vchiq_release_message(instance->vchiq_instance, instance->service_handle, rmsg_handle);
return ret;
}
static int destroy_component(struct vchiq_mmal_instance *instance,
struct vchiq_mmal_component *component)
{
int ret;
struct mmal_msg m;
struct mmal_msg *rmsg;
struct vchiq_header *rmsg_handle;
m.h.type = MMAL_MSG_TYPE_COMPONENT_DESTROY;
m.u.component_destroy.component_handle = component->handle;
ret = send_synchronous_mmal_msg(instance, &m,
sizeof(m.u.component_destroy),
&rmsg, &rmsg_handle);
if (ret)
return ret;
if (rmsg->h.type != m.h.type) {
ret = -EINVAL;
goto release_msg;
}
ret = -rmsg->u.component_destroy_reply.status;
release_msg:
vchiq_release_message(instance->vchiq_instance, instance->service_handle, rmsg_handle);
return ret;
}
static int enable_component(struct vchiq_mmal_instance *instance,
struct vchiq_mmal_component *component)
{
int ret;
struct mmal_msg m;
struct mmal_msg *rmsg;
struct vchiq_header *rmsg_handle;
m.h.type = MMAL_MSG_TYPE_COMPONENT_ENABLE;
m.u.component_enable.component_handle = component->handle;
ret = send_synchronous_mmal_msg(instance, &m,
sizeof(m.u.component_enable),
&rmsg, &rmsg_handle);
if (ret)
return ret;
if (rmsg->h.type != m.h.type) {
ret = -EINVAL;
goto release_msg;
}
ret = -rmsg->u.component_enable_reply.status;
release_msg:
vchiq_release_message(instance->vchiq_instance, instance->service_handle, rmsg_handle);
return ret;
}
static int disable_component(struct vchiq_mmal_instance *instance,
struct vchiq_mmal_component *component)
{
int ret;
struct mmal_msg m;
struct mmal_msg *rmsg;
struct vchiq_header *rmsg_handle;
m.h.type = MMAL_MSG_TYPE_COMPONENT_DISABLE;
m.u.component_disable.component_handle = component->handle;
ret = send_synchronous_mmal_msg(instance, &m,
sizeof(m.u.component_disable),
&rmsg, &rmsg_handle);
if (ret)
return ret;
if (rmsg->h.type != m.h.type) {
ret = -EINVAL;
goto release_msg;
}
ret = -rmsg->u.component_disable_reply.status;
release_msg:
vchiq_release_message(instance->vchiq_instance, instance->service_handle, rmsg_handle);
return ret;
}
static int get_version(struct vchiq_mmal_instance *instance,
u32 *major_out, u32 *minor_out)
{
int ret;
struct mmal_msg m;
struct mmal_msg *rmsg;
struct vchiq_header *rmsg_handle;
m.h.type = MMAL_MSG_TYPE_GET_VERSION;
ret = send_synchronous_mmal_msg(instance, &m,
sizeof(m.u.version),
&rmsg, &rmsg_handle);
if (ret)
return ret;
if (rmsg->h.type != m.h.type) {
ret = -EINVAL;
goto release_msg;
}
*major_out = rmsg->u.version.major;
*minor_out = rmsg->u.version.minor;
release_msg:
vchiq_release_message(instance->vchiq_instance, instance->service_handle, rmsg_handle);
return ret;
}
static int port_action_port(struct vchiq_mmal_instance *instance,
struct vchiq_mmal_port *port,
enum mmal_msg_port_action_type action_type)
{
int ret;
struct mmal_msg m;
struct mmal_msg *rmsg;
struct vchiq_header *rmsg_handle;
m.h.type = MMAL_MSG_TYPE_PORT_ACTION;
m.u.port_action_port.component_handle = port->component->handle;
m.u.port_action_port.port_handle = port->handle;
m.u.port_action_port.action = action_type;
port_to_mmal_msg(port, &m.u.port_action_port.port);
ret = send_synchronous_mmal_msg(instance, &m,
sizeof(m.u.port_action_port),
&rmsg, &rmsg_handle);
if (ret)
return ret;
if (rmsg->h.type != MMAL_MSG_TYPE_PORT_ACTION) {
ret = -EINVAL;
goto release_msg;
}
ret = -rmsg->u.port_action_reply.status;
pr_debug("%s:result:%d component:0x%x port:%d action:%s(%d)\n",
__func__,
ret, port->component->handle, port->handle,
port_action_type_names[action_type], action_type);
release_msg:
vchiq_release_message(instance->vchiq_instance, instance->service_handle, rmsg_handle);
return ret;
}
static int port_action_handle(struct vchiq_mmal_instance *instance,
struct vchiq_mmal_port *port,
enum mmal_msg_port_action_type action_type,
u32 connect_component_handle,
u32 connect_port_handle)
{
int ret;
struct mmal_msg m;
struct mmal_msg *rmsg;
struct vchiq_header *rmsg_handle;
m.h.type = MMAL_MSG_TYPE_PORT_ACTION;
m.u.port_action_handle.component_handle = port->component->handle;
m.u.port_action_handle.port_handle = port->handle;
m.u.port_action_handle.action = action_type;
m.u.port_action_handle.connect_component_handle =
connect_component_handle;
m.u.port_action_handle.connect_port_handle = connect_port_handle;
ret = send_synchronous_mmal_msg(instance, &m,
sizeof(m.u.port_action_handle),
&rmsg, &rmsg_handle);
if (ret)
return ret;
if (rmsg->h.type != MMAL_MSG_TYPE_PORT_ACTION) {
ret = -EINVAL;
goto release_msg;
}
ret = -rmsg->u.port_action_reply.status;
pr_debug("%s:result:%d component:0x%x port:%d action:%s(%d) connect component:0x%x connect port:%d\n",
__func__,
ret, port->component->handle, port->handle,
port_action_type_names[action_type],
action_type, connect_component_handle, connect_port_handle);
release_msg:
vchiq_release_message(instance->vchiq_instance, instance->service_handle, rmsg_handle);
return ret;
}
static int port_parameter_set(struct vchiq_mmal_instance *instance,
struct vchiq_mmal_port *port,
u32 parameter_id, void *value, u32 value_size)
{
int ret;
struct mmal_msg m;
struct mmal_msg *rmsg;
struct vchiq_header *rmsg_handle;
m.h.type = MMAL_MSG_TYPE_PORT_PARAMETER_SET;
m.u.port_parameter_set.component_handle = port->component->handle;
m.u.port_parameter_set.port_handle = port->handle;
m.u.port_parameter_set.id = parameter_id;
m.u.port_parameter_set.size = (2 * sizeof(u32)) + value_size;
memcpy(&m.u.port_parameter_set.value, value, value_size);
ret = send_synchronous_mmal_msg(instance, &m,
(4 * sizeof(u32)) + value_size,
&rmsg, &rmsg_handle);
if (ret)
return ret;
if (rmsg->h.type != MMAL_MSG_TYPE_PORT_PARAMETER_SET) {
ret = -EINVAL;
goto release_msg;
}
ret = -rmsg->u.port_parameter_set_reply.status;
pr_debug("%s:result:%d component:0x%x port:%d parameter:%d\n",
__func__,
ret, port->component->handle, port->handle, parameter_id);
release_msg:
vchiq_release_message(instance->vchiq_instance, instance->service_handle, rmsg_handle);
return ret;
}
static int port_parameter_get(struct vchiq_mmal_instance *instance,
struct vchiq_mmal_port *port,
u32 parameter_id, void *value, u32 *value_size)
{
int ret;
struct mmal_msg m;
struct mmal_msg *rmsg;
struct vchiq_header *rmsg_handle;
m.h.type = MMAL_MSG_TYPE_PORT_PARAMETER_GET;
m.u.port_parameter_get.component_handle = port->component->handle;
m.u.port_parameter_get.port_handle = port->handle;
m.u.port_parameter_get.id = parameter_id;
m.u.port_parameter_get.size = (2 * sizeof(u32)) + *value_size;
ret = send_synchronous_mmal_msg(instance, &m,
sizeof(struct
mmal_msg_port_parameter_get),
&rmsg, &rmsg_handle);
if (ret)
return ret;
if (rmsg->h.type != MMAL_MSG_TYPE_PORT_PARAMETER_GET) {
pr_err("Incorrect reply type %d\n", rmsg->h.type);
ret = -EINVAL;
goto release_msg;
}
ret = rmsg->u.port_parameter_get_reply.status;
rmsg->u.port_parameter_get_reply.size -= (2 * sizeof(u32));
if (ret || rmsg->u.port_parameter_get_reply.size > *value_size) {
memcpy(value, &rmsg->u.port_parameter_get_reply.value,
*value_size);
} else {
memcpy(value, &rmsg->u.port_parameter_get_reply.value,
rmsg->u.port_parameter_get_reply.size);
}
*value_size = rmsg->u.port_parameter_get_reply.size;
pr_debug("%s:result:%d component:0x%x port:%d parameter:%d\n", __func__,
ret, port->component->handle, port->handle, parameter_id);
release_msg:
vchiq_release_message(instance->vchiq_instance, instance->service_handle, rmsg_handle);
return ret;
}
static int port_disable(struct vchiq_mmal_instance *instance,
struct vchiq_mmal_port *port)
{
int ret;
struct list_head *q, *buf_head;
unsigned long flags = 0;
if (!port->enabled)
return 0;
port->enabled = false;
ret = port_action_port(instance, port,
MMAL_MSG_PORT_ACTION_TYPE_DISABLE);
if (ret == 0) {
spin_lock_irqsave(&port->slock, flags);
list_for_each_safe(buf_head, q, &port->buffers) {
struct mmal_buffer *mmalbuf;
mmalbuf = list_entry(buf_head, struct mmal_buffer,
list);
list_del(buf_head);
if (port->buffer_cb) {
mmalbuf->length = 0;
mmalbuf->mmal_flags = 0;
mmalbuf->dts = MMAL_TIME_UNKNOWN;
mmalbuf->pts = MMAL_TIME_UNKNOWN;
port->buffer_cb(instance,
port, 0, mmalbuf);
}
}
spin_unlock_irqrestore(&port->slock, flags);
ret = port_info_get(instance, port);
}
return ret;
}
static int port_enable(struct vchiq_mmal_instance *instance,
struct vchiq_mmal_port *port)
{
unsigned int hdr_count;
struct list_head *q, *buf_head;
int ret;
if (port->enabled)
return 0;
ret = port_action_port(instance, port,
MMAL_MSG_PORT_ACTION_TYPE_ENABLE);
if (ret)
goto done;
port->enabled = true;
if (port->buffer_cb) {
hdr_count = 1;
list_for_each_safe(buf_head, q, &port->buffers) {
struct mmal_buffer *mmalbuf;
mmalbuf = list_entry(buf_head, struct mmal_buffer,
list);
ret = buffer_from_host(instance, port, mmalbuf);
if (ret)
goto done;
list_del(buf_head);
hdr_count++;
if (hdr_count > port->current_buffer.num)
break;
}
}
ret = port_info_get(instance, port);
done:
return ret;
}
int vchiq_mmal_port_set_format(struct vchiq_mmal_instance *instance,
struct vchiq_mmal_port *port)
{
int ret;
if (mutex_lock_interruptible(&instance->vchiq_mutex))
return -EINTR;
ret = port_info_set(instance, port);
if (ret)
goto release_unlock;
ret = port_info_get(instance, port);
release_unlock:
mutex_unlock(&instance->vchiq_mutex);
return ret;
}
EXPORT_SYMBOL_GPL(vchiq_mmal_port_set_format);
int vchiq_mmal_port_parameter_set(struct vchiq_mmal_instance *instance,
struct vchiq_mmal_port *port,
u32 parameter, void *value, u32 value_size)
{
int ret;
if (mutex_lock_interruptible(&instance->vchiq_mutex))
return -EINTR;
ret = port_parameter_set(instance, port, parameter, value, value_size);
mutex_unlock(&instance->vchiq_mutex);
return ret;
}
EXPORT_SYMBOL_GPL(vchiq_mmal_port_parameter_set);
int vchiq_mmal_port_parameter_get(struct vchiq_mmal_instance *instance,
struct vchiq_mmal_port *port,
u32 parameter, void *value, u32 *value_size)
{
int ret;
if (mutex_lock_interruptible(&instance->vchiq_mutex))
return -EINTR;
ret = port_parameter_get(instance, port, parameter, value, value_size);
mutex_unlock(&instance->vchiq_mutex);
return ret;
}
EXPORT_SYMBOL_GPL(vchiq_mmal_port_parameter_get);
int vchiq_mmal_port_enable(struct vchiq_mmal_instance *instance,
struct vchiq_mmal_port *port,
vchiq_mmal_buffer_cb buffer_cb)
{
int ret;
if (mutex_lock_interruptible(&instance->vchiq_mutex))
return -EINTR;
if (port->enabled) {
ret = 0;
goto unlock;
}
port->buffer_cb = buffer_cb;
ret = port_enable(instance, port);
unlock:
mutex_unlock(&instance->vchiq_mutex);
return ret;
}
EXPORT_SYMBOL_GPL(vchiq_mmal_port_enable);
int vchiq_mmal_port_disable(struct vchiq_mmal_instance *instance,
struct vchiq_mmal_port *port)
{
int ret;
if (mutex_lock_interruptible(&instance->vchiq_mutex))
return -EINTR;
if (!port->enabled) {
mutex_unlock(&instance->vchiq_mutex);
return 0;
}
ret = port_disable(instance, port);
mutex_unlock(&instance->vchiq_mutex);
return ret;
}
EXPORT_SYMBOL_GPL(vchiq_mmal_port_disable);
int vchiq_mmal_port_connect_tunnel(struct vchiq_mmal_instance *instance,
struct vchiq_mmal_port *src,
struct vchiq_mmal_port *dst)
{
int ret;
if (mutex_lock_interruptible(&instance->vchiq_mutex))
return -EINTR;
if (src->connected) {
ret = port_disable(instance, src);
if (ret) {
pr_err("failed disabling src port(%d)\n", ret);
goto release_unlock;
}
ret = port_action_handle(instance, src,
MMAL_MSG_PORT_ACTION_TYPE_DISCONNECT,
src->connected->component->handle,
src->connected->handle);
if (ret < 0) {
pr_err("failed disconnecting src port\n");
goto release_unlock;
}
src->connected->enabled = false;
src->connected = NULL;
}
if (!dst) {
ret = 0;
pr_debug("not making new connection\n");
goto release_unlock;
}
dst->format.encoding = src->format.encoding;
dst->es.video.width = src->es.video.width;
dst->es.video.height = src->es.video.height;
dst->es.video.crop.x = src->es.video.crop.x;
dst->es.video.crop.y = src->es.video.crop.y;
dst->es.video.crop.width = src->es.video.crop.width;
dst->es.video.crop.height = src->es.video.crop.height;
dst->es.video.frame_rate.numerator = src->es.video.frame_rate.numerator;
dst->es.video.frame_rate.denominator = src->es.video.frame_rate.denominator;
ret = port_info_set(instance, dst);
if (ret) {
pr_debug("setting port info failed\n");
goto release_unlock;
}
ret = port_info_get(instance, dst);
if (ret) {
pr_debug("read back port info failed\n");
goto release_unlock;
}
ret = port_action_handle(instance, src,
MMAL_MSG_PORT_ACTION_TYPE_CONNECT,
dst->component->handle, dst->handle);
if (ret < 0) {
pr_debug("connecting port %d:%d to %d:%d failed\n",
src->component->handle, src->handle,
dst->component->handle, dst->handle);
goto release_unlock;
}
src->connected = dst;
release_unlock:
mutex_unlock(&instance->vchiq_mutex);
return ret;
}
EXPORT_SYMBOL_GPL(vchiq_mmal_port_connect_tunnel);
int vchiq_mmal_submit_buffer(struct vchiq_mmal_instance *instance,
struct vchiq_mmal_port *port,
struct mmal_buffer *buffer)
{
unsigned long flags = 0;
int ret;
ret = buffer_from_host(instance, port, buffer);
if (ret == -EINVAL) {
spin_lock_irqsave(&port->slock, flags);
list_add_tail(&buffer->list, &port->buffers);
spin_unlock_irqrestore(&port->slock, flags);
}
return 0;
}
EXPORT_SYMBOL_GPL(vchiq_mmal_submit_buffer);
int mmal_vchi_buffer_init(struct vchiq_mmal_instance *instance,
struct mmal_buffer *buf)
{
struct mmal_msg_context *msg_context = get_msg_context(instance);
if (IS_ERR(msg_context))
return (PTR_ERR(msg_context));
buf->msg_context = msg_context;
return 0;
}
EXPORT_SYMBOL_GPL(mmal_vchi_buffer_init);
int mmal_vchi_buffer_cleanup(struct mmal_buffer *buf)
{
struct mmal_msg_context *msg_context = buf->msg_context;
if (msg_context)
release_msg_context(msg_context);
buf->msg_context = NULL;
return 0;
}
EXPORT_SYMBOL_GPL(mmal_vchi_buffer_cleanup);
int vchiq_mmal_component_init(struct vchiq_mmal_instance *instance,
const char *name,
struct vchiq_mmal_component **component_out)
{
int ret;
int idx;
struct vchiq_mmal_component *component = NULL;
if (mutex_lock_interruptible(&instance->vchiq_mutex))
return -EINTR;
for (idx = 0; idx < VCHIQ_MMAL_MAX_COMPONENTS; idx++) {
if (!instance->component[idx].in_use) {
component = &instance->component[idx];
component->in_use = true;
break;
}
}
if (!component) {
ret = -EINVAL;
goto unlock;
}
component->client_component = idx;
ret = create_component(instance, component, name);
if (ret < 0) {
pr_err("%s: failed to create component %d (Not enough GPU mem?)\n",
__func__, ret);
goto unlock;
}
component->control.type = MMAL_PORT_TYPE_CONTROL;
component->control.index = 0;
component->control.component = component;
spin_lock_init(&component->control.slock);
INIT_LIST_HEAD(&component->control.buffers);
ret = port_info_get(instance, &component->control);
if (ret < 0)
goto release_component;
for (idx = 0; idx < component->inputs; idx++) {
component->input[idx].type = MMAL_PORT_TYPE_INPUT;
component->input[idx].index = idx;
component->input[idx].component = component;
spin_lock_init(&component->input[idx].slock);
INIT_LIST_HEAD(&component->input[idx].buffers);
ret = port_info_get(instance, &component->input[idx]);
if (ret < 0)
goto release_component;
}
for (idx = 0; idx < component->outputs; idx++) {
component->output[idx].type = MMAL_PORT_TYPE_OUTPUT;
component->output[idx].index = idx;
component->output[idx].component = component;
spin_lock_init(&component->output[idx].slock);
INIT_LIST_HEAD(&component->output[idx].buffers);
ret = port_info_get(instance, &component->output[idx]);
if (ret < 0)
goto release_component;
}
for (idx = 0; idx < component->clocks; idx++) {
component->clock[idx].type = MMAL_PORT_TYPE_CLOCK;
component->clock[idx].index = idx;
component->clock[idx].component = component;
spin_lock_init(&component->clock[idx].slock);
INIT_LIST_HEAD(&component->clock[idx].buffers);
ret = port_info_get(instance, &component->clock[idx]);
if (ret < 0)
goto release_component;
}
*component_out = component;
mutex_unlock(&instance->vchiq_mutex);
return 0;
release_component:
destroy_component(instance, component);
unlock:
if (component)
component->in_use = false;
mutex_unlock(&instance->vchiq_mutex);
return ret;
}
EXPORT_SYMBOL_GPL(vchiq_mmal_component_init);
int vchiq_mmal_component_finalise(struct vchiq_mmal_instance *instance,
struct vchiq_mmal_component *component)
{
int ret;
if (mutex_lock_interruptible(&instance->vchiq_mutex))
return -EINTR;
if (component->enabled)
ret = disable_component(instance, component);
ret = destroy_component(instance, component);
component->in_use = false;
mutex_unlock(&instance->vchiq_mutex);
return ret;
}
EXPORT_SYMBOL_GPL(vchiq_mmal_component_finalise);
int vchiq_mmal_component_enable(struct vchiq_mmal_instance *instance,
struct vchiq_mmal_component *component)
{
int ret;
if (mutex_lock_interruptible(&instance->vchiq_mutex))
return -EINTR;
if (component->enabled) {
mutex_unlock(&instance->vchiq_mutex);
return 0;
}
ret = enable_component(instance, component);
if (ret == 0)
component->enabled = true;
mutex_unlock(&instance->vchiq_mutex);
return ret;
}
EXPORT_SYMBOL_GPL(vchiq_mmal_component_enable);
int vchiq_mmal_component_disable(struct vchiq_mmal_instance *instance,
struct vchiq_mmal_component *component)
{
int ret;
if (mutex_lock_interruptible(&instance->vchiq_mutex))
return -EINTR;
if (!component->enabled) {
mutex_unlock(&instance->vchiq_mutex);
return 0;
}
ret = disable_component(instance, component);
if (ret == 0)
component->enabled = false;
mutex_unlock(&instance->vchiq_mutex);
return ret;
}
EXPORT_SYMBOL_GPL(vchiq_mmal_component_disable);
int vchiq_mmal_version(struct vchiq_mmal_instance *instance,
u32 *major_out, u32 *minor_out)
{
int ret;
if (mutex_lock_interruptible(&instance->vchiq_mutex))
return -EINTR;
ret = get_version(instance, major_out, minor_out);
mutex_unlock(&instance->vchiq_mutex);
return ret;
}
EXPORT_SYMBOL_GPL(vchiq_mmal_version);
int vchiq_mmal_finalise(struct vchiq_mmal_instance *instance)
{
int status = 0;
if (!instance)
return -EINVAL;
if (mutex_lock_interruptible(&instance->vchiq_mutex))
return -EINTR;
vchiq_use_service(instance->vchiq_instance, instance->service_handle);
status = vchiq_close_service(instance->vchiq_instance, instance->service_handle);
if (status != 0)
pr_err("mmal-vchiq: VCHIQ close failed\n");
mutex_unlock(&instance->vchiq_mutex);
vchiq_shutdown(instance->vchiq_instance);
destroy_workqueue(instance->bulk_wq);
idr_destroy(&instance->context_map);
kfree(instance);
return status;
}
EXPORT_SYMBOL_GPL(vchiq_mmal_finalise);
int vchiq_mmal_init(struct vchiq_mmal_instance **out_instance)
{
int status;
int err = -ENODEV;
struct vchiq_mmal_instance *instance;
struct vchiq_instance *vchiq_instance;
struct vchiq_service_params_kernel params = {
.version = VC_MMAL_VER,
.version_min = VC_MMAL_MIN_VER,
.fourcc = VCHIQ_MAKE_FOURCC('m', 'm', 'a', 'l'),
.callback = service_callback,
.userdata = NULL,
};
BUILD_BUG_ON(sizeof(struct mmal_msg_header) != 24);
BUILD_BUG_ON(sizeof(struct mmal_msg) > MMAL_MSG_MAX_SIZE);
BUILD_BUG_ON(sizeof(struct mmal_port) != 64);
status = vchiq_initialise(&vchiq_instance);
if (status) {
pr_err("Failed to initialise VCHI instance (status=%d)\n",
status);
return -EIO;
}
status = vchiq_connect(vchiq_instance);
if (status) {
pr_err("Failed to connect VCHI instance (status=%d)\n", status);
err = -EIO;
goto err_shutdown_vchiq;
}
instance = kzalloc(sizeof(*instance), GFP_KERNEL);
if (!instance) {
err = -ENOMEM;
goto err_shutdown_vchiq;
}
mutex_init(&instance->vchiq_mutex);
instance->vchiq_instance = vchiq_instance;
mutex_init(&instance->context_map_lock);
idr_init_base(&instance->context_map, 1);
params.userdata = instance;
instance->bulk_wq = alloc_ordered_workqueue("mmal-vchiq",
WQ_MEM_RECLAIM);
if (!instance->bulk_wq)
goto err_free;
status = vchiq_open_service(vchiq_instance, ¶ms,
&instance->service_handle);
if (status) {
pr_err("Failed to open VCHI service connection (status=%d)\n",
status);
goto err_close_services;
}
vchiq_release_service(instance->vchiq_instance, instance->service_handle);
*out_instance = instance;
return 0;
err_close_services:
vchiq_close_service(instance->vchiq_instance, instance->service_handle);
destroy_workqueue(instance->bulk_wq);
err_free:
kfree(instance);
err_shutdown_vchiq:
vchiq_shutdown(vchiq_instance);
return err;
}
EXPORT_SYMBOL_GPL(vchiq_mmal_init);
MODULE_DESCRIPTION("BCM2835 MMAL VCHIQ interface");
MODULE_AUTHOR("Dave Stevenson, <dave.stevenson@raspberrypi.org>");
MODULE_LICENSE("GPL"