From e0f91c62fa97978bb375ccb929fc221eb48c94e0 Mon Sep 17 00:00:00 2001 From: Deva Ramasubramanian Date: Tue, 31 Jul 2012 11:11:15 -0700 Subject: [PATCH] msm: vidc: wfd: Add WFD support for msm8974 Adds three new subdevices: - Encoder subdevice to interface with new venus driver o Renamed enc-subdev.c to enc-mfc-subdev.c which only compiles for targets using the Samsung MFC core. - MDP subdevice to interface with new MDSS driver o Renamed mdp-subdev.c to mdp-4-subdev.c used for targets using MDP4. - MDP subdevice for debugging purposes - Introduce a Kconfig file to properly choose between subdevices according to target Change-Id: I125b39cfdeb1fbb4adf7c15d0c4452146764f985 Signed-off-by: Deva Ramasubramanian Signed-off-by: Amara Venkata Mastan Manoj Kumar --- arch/arm/configs/msm8974_defconfig | 5 + drivers/media/video/Kconfig | 1 + drivers/media/video/Makefile | 2 +- drivers/media/video/msm_wfd/Kconfig | 6 + drivers/media/video/msm_wfd/Makefile | 19 +- .../{enc-subdev.c => enc-mfc-subdev.c} | 27 +- drivers/media/video/msm_wfd/enc-subdev.h | 20 +- .../media/video/msm_wfd/enc-venus-subdev.c | 1139 +++++++++++++++++ .../msm_wfd/{mdp-subdev.c => mdp-4-subdev.c} | 27 +- drivers/media/video/msm_wfd/mdp-5-subdev.c | 229 ++++ .../media/video/msm_wfd/mdp-dummy-subdev.c | 162 +++ drivers/media/video/msm_wfd/mdp-subdev.h | 6 + drivers/media/video/msm_wfd/wfd-ioctl.c | 15 +- drivers/media/video/msm_wfd/wfd-util.h | 15 +- 14 files changed, 1627 insertions(+), 46 deletions(-) create mode 100644 drivers/media/video/msm_wfd/Kconfig rename drivers/media/video/msm_wfd/{enc-subdev.c => enc-mfc-subdev.c} (99%) create mode 100644 drivers/media/video/msm_wfd/enc-venus-subdev.c rename drivers/media/video/msm_wfd/{mdp-subdev.c => mdp-4-subdev.c} (90%) create mode 100644 drivers/media/video/msm_wfd/mdp-5-subdev.c create mode 100644 drivers/media/video/msm_wfd/mdp-dummy-subdev.c diff --git a/arch/arm/configs/msm8974_defconfig b/arch/arm/configs/msm8974_defconfig index a559fca55918..7a71a36f2ae4 100644 --- a/arch/arm/configs/msm8974_defconfig +++ b/arch/arm/configs/msm8974_defconfig @@ -171,6 +171,11 @@ CONFIG_VIDEOBUF2_MSM_MEM=y # CONFIG_RADIO_ADAPTERS is not set CONFIG_V4L_PLATFORM_DRIVERS=y CONFIG_MSM_CAMERA_V4L2=y +<<<<<<< HEAD +======= +CONFIG_MSM_WFD=y +CONFIG_OV2720=y +>>>>>>> 1efb995... msm: vidc: wfd: Add WFD support for msm8974 CONFIG_MSM_CAMERA_SENSOR=y CONFIG_MSM_ACTUATOR=y CONFIG_MSM_CAM_IRQ_ROUTER=n diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index ae020e844cbb..0d2d91c1c8cc 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -1262,3 +1262,4 @@ config VIDEO_MX2_EMMAPRP endif # V4L_MEM2MEM_DRIVERS source "drivers/media/video/msm_vidc/Kconfig" +source "drivers/media/video/msm_wfd/Kconfig" diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index 082ee3d098b1..fd736c3d44f3 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -215,7 +215,7 @@ obj-y += davinci/ obj-$(CONFIG_MSM_CAMERA) += msm/ obj-$(CONFIG_ARCH_OMAP) += omap/ obj-$(CONFIG_MSM_VIDC_V4L2) += msm_vidc/ -obj-$(CONFIG_FB_MSM_WRITEBACK_MSM_PANEL) += msm_wfd/ +obj-$(CONFIG_MSM_WFD) += msm_wfd/ ccflags-y += -I$(srctree)/drivers/media/dvb/dvb-core ccflags-y += -I$(srctree)/drivers/media/dvb/frontends diff --git a/drivers/media/video/msm_wfd/Kconfig b/drivers/media/video/msm_wfd/Kconfig new file mode 100644 index 000000000000..6050d73cc810 --- /dev/null +++ b/drivers/media/video/msm_wfd/Kconfig @@ -0,0 +1,6 @@ +menuconfig MSM_WFD + bool "Qualcomm MSM Wifi Display Driver" + depends on (MSM_VIDC_1080P || MSM_VIDC_V4L2) + ---help--- + Enables the Wifi Display driver. + diff --git a/drivers/media/video/msm_wfd/Makefile b/drivers/media/video/msm_wfd/Makefile index 5decaca12b43..813bc6463381 100644 --- a/drivers/media/video/msm_wfd/Makefile +++ b/drivers/media/video/msm_wfd/Makefile @@ -1,5 +1,14 @@ -obj-y += mdp-subdev.o -obj-y += enc-subdev.o -obj-y += vsg-subdev.o -obj-y += wfd-ioctl.o -obj-y += wfd-util.o +ifeq ($(CONFIG_MSM_WFD),y) + obj-y += wfd-ioctl.o + obj-y += wfd-util.o + obj-y += vsg-subdev.o + ifeq ($(CONFIG_FB_MSM_WRITEBACK_MSM_PANEL),y) + obj-y += mdp-4-subdev.o + else ifeq ($(CONFIG_FB_MSM_MDSS_WRITEBACK),y) + obj-y += mdp-5-subdev.o + else + obj-y += mdp-dummy-subdev.o + endif + obj-$(CONFIG_MSM_VIDC_1080P) += enc-mfc-subdev.o + obj-$(CONFIG_MSM_VIDC_V4L2) += enc-venus-subdev.o +endif diff --git a/drivers/media/video/msm_wfd/enc-subdev.c b/drivers/media/video/msm_wfd/enc-mfc-subdev.c similarity index 99% rename from drivers/media/video/msm_wfd/enc-subdev.c rename to drivers/media/video/msm_wfd/enc-mfc-subdev.c index e1cabf93d355..820d21e586eb 100644 --- a/drivers/media/video/msm_wfd/enc-subdev.c +++ b/drivers/media/video/msm_wfd/enc-mfc-subdev.c @@ -227,7 +227,7 @@ static void venc_cb(u32 event, u32 status, void *info, u32 size, void *handle, BUFFER_TYPE_OUTPUT, pmem_fd, kvaddr, buffer_index, &ion_handle); else - WFD_MSG_ERR("Got an output buffer that we " + WFD_MSG_ERR("Got an output buffer that we " \ "couldn't recognize!\n"); if (msm_ion_do_cache_op(client_ctx->user_ion_client, @@ -547,8 +547,8 @@ static long venc_set_codec_level(struct video_client_ctx *client_ctx, if (vcd_property_level.level < VCD_LEVEL_MPEG4_0 || vcd_property_level.level > VCD_LEVEL_MPEG4_X) { - WFD_MSG_ERR("Level (%d) out of range" - "for codec (%d)\n", level, codec); + WFD_MSG_ERR("Level (%d) out of range for codec (%d)\n", + level, codec); rc = -EINVAL; goto err; @@ -558,8 +558,8 @@ static long venc_set_codec_level(struct video_client_ctx *client_ctx, if (vcd_property_level.level < VCD_LEVEL_H264_1 || vcd_property_level.level > VCD_LEVEL_H264_5p1) { - WFD_MSG_ERR("Level (%d) out of range" - "for codec (%d)\n", level, codec); + WFD_MSG_ERR("Level (%d) out of range for codec (%d)\n", + level, codec); rc = -EINVAL; goto err; @@ -678,9 +678,9 @@ static long venc_set_codec_profile(struct video_client_ctx *client_ctx, vcd_property_profile.profile = VCD_PROFILE_MPEG4_ASP; break; default: - WFD_MSG_ERR("Profile %d not supported," - "defaulting to simple (%d)", - profile, VCD_PROFILE_MPEG4_SP); + WFD_MSG_ERR("Profile %d not supported, defaulting " \ + "to simple (%d)", profile, + VCD_PROFILE_MPEG4_SP); vcd_property_profile.profile = VCD_PROFILE_MPEG4_SP; break; } @@ -697,17 +697,16 @@ static long venc_set_codec_profile(struct video_client_ctx *client_ctx, vcd_property_profile.profile = VCD_PROFILE_H264_HIGH; break; default: - WFD_MSG_ERR("Profile %d not supported," - "defaulting to baseline (%d)", - profile, VCD_PROFILE_H264_BASELINE); + WFD_MSG_ERR("Profile %d not supported, defaulting " \ + "to baseline (%d)", profile, + VCD_PROFILE_H264_BASELINE); vcd_property_profile.profile = VCD_PROFILE_H264_BASELINE; break; } } else { - WFD_MSG_ERR("Codec (%d) not supported," - "not setting profile (%d)", - codec, profile); + WFD_MSG_ERR("Codec (%d) not supported, not "\ + "setting profile (%d)", codec, profile); rc = -ENOTSUPP; goto err_set_profile; } diff --git a/drivers/media/video/msm_wfd/enc-subdev.h b/drivers/media/video/msm_wfd/enc-subdev.h index 5873e6253d8b..6418af6da8fb 100644 --- a/drivers/media/video/msm_wfd/enc-subdev.h +++ b/drivers/media/video/msm_wfd/enc-subdev.h @@ -14,7 +14,8 @@ #ifndef _WFD_ENC_SUBDEV_ #define _WFD_ENC_SUBDEV_ -#include +#include +#include #include #include #define VENC_MAGIC_IOCTL 'V' @@ -29,6 +30,7 @@ struct mem_region { u32 cookie; struct ion_handle *ion_handle; }; + struct bufreq { u32 count; u32 height; @@ -44,13 +46,27 @@ struct venc_buf_info { struct venc_msg_ops { void *cookie; void *cbdata; - int secure; + bool secure; void (*op_buffer_done)(void *cookie, u32 status, struct vb2_buffer *buf); void (*ip_buffer_done)(void *cookie, u32 status, struct mem_region *mregion); }; +static inline bool mem_region_equals(struct mem_region *a, + struct mem_region *b) +{ + if (a == b) + return true; + else if (a->fd || b->fd) + return (a->fd == b->fd) && + (a->offset == b->offset); + else if (a->kvaddr || b->kvaddr) + return a->kvaddr == b->kvaddr; + else + return false; +} + #define OPEN _IOR('V', 1, void *) #define CLOSE _IO('V', 2) #define ENCODE_START _IO('V', 3) diff --git a/drivers/media/video/msm_wfd/enc-venus-subdev.c b/drivers/media/video/msm_wfd/enc-venus-subdev.c new file mode 100644 index 000000000000..47f4b1358a86 --- /dev/null +++ b/drivers/media/video/msm_wfd/enc-venus-subdev.c @@ -0,0 +1,1139 @@ +/* Copyright (c) 2012, The Linux Foundation. All rights reserved. +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 and +* only version 2 as published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "enc-subdev.h" +#include "wfd-util.h" + +#define BUF_TYPE_OUTPUT V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE +#define BUF_TYPE_INPUT V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE + +static struct ion_client *venc_ion_client; + +struct index_bitmap { + unsigned long *bitmap; + int size; + int size_bits; /*Size in bits, not necessarily size/8 */ +}; + +struct venc_inst { + void *vidc_context; + struct mutex lock; + struct venc_msg_ops vmops; + struct mem_region registered_input_bufs, registered_output_bufs; + struct index_bitmap free_input_indices, free_output_indices; + int num_output_planes, num_input_planes; + struct task_struct *callback_thread; + bool callback_thread_running; + struct completion dq_complete, cmd_complete; + bool secure; + int domain; +}; + +int venc_load_fw(struct v4l2_subdev *sd) +{ + /*No need to explicitly load the fw */ + return 0; +} + +int venc_init(struct v4l2_subdev *sd, u32 val) +{ + if (!venc_ion_client) + venc_ion_client = msm_ion_client_create(-1, "wfd_enc_subdev"); + + return venc_ion_client ? 0 : -ENOMEM; +} + +static int next_free_index(struct index_bitmap *index_bitmap) +{ + int index = find_first_zero_bit(index_bitmap->bitmap, + index_bitmap->size_bits); + + return (index >= index_bitmap->size_bits) ? + -1 : index; +} + +static int mark_index_busy(struct index_bitmap *index_bitmap, int index) +{ + if (index > index_bitmap->size_bits) { + WFD_MSG_WARN("Marking unknown index as busy\n"); + return -EINVAL; + } + set_bit(index, index_bitmap->bitmap); + return 0; +} + +static int mark_index_free(struct index_bitmap *index_bitmap, int index) +{ + if (index > index_bitmap->size_bits) { + WFD_MSG_WARN("Marking unknown index as free\n"); + return -EINVAL; + } + clear_bit(index, index_bitmap->bitmap); + return 0; +} + +static int get_list_len(struct mem_region *list) +{ + struct mem_region *curr = NULL; + int index = 0; + list_for_each_entry(curr, &list->list, list) { + ++index; + } + + return index; +} + +static struct mem_region *get_registered_mregion(struct mem_region *list, + struct mem_region *mregion) +{ + struct mem_region *curr = NULL; + list_for_each_entry(curr, &list->list, list) { + if (unlikely(mem_region_equals(curr, mregion))) + return curr; + } + + return NULL; +} + +static int venc_vidc_callback_thread(void *data) +{ + struct venc_inst *inst = data; + WFD_MSG_DBG("Starting callback thread\n"); + while (!kthread_should_stop()) { + bool dequeue_buf = false; + struct v4l2_buffer buffer = {0}; + struct v4l2_event event = {0}; + int num_planes = 0; + int flags = msm_vidc_wait(inst->vidc_context); + + if (flags & POLLERR) { + WFD_MSG_ERR("Encoder reported error\n"); + break; + } + + if (flags & POLLPRI) { + bool bail_out = false; + + msm_vidc_dqevent(inst->vidc_context, &event); + if (event.type == V4L2_EVENT_MSM_VIDC_CLOSE_DONE) { + WFD_MSG_ERR("enc callback thread shutting " \ + "down normally\n"); + bail_out = true; + } else { + WFD_MSG_ERR("Got unknown event %d, ignoring\n", + event.id); + } + + complete_all(&inst->cmd_complete); + if (bail_out) + break; + } + + if (flags & POLLIN || flags & POLLRDNORM) { + buffer.type = BUF_TYPE_OUTPUT; + dequeue_buf = true; + num_planes = inst->num_output_planes; + WFD_MSG_DBG("Output buffer ready!\n"); + } + + if (flags & POLLOUT || flags & POLLWRNORM) { + buffer.type = BUF_TYPE_INPUT; + dequeue_buf = true; + num_planes = inst->num_input_planes; + WFD_MSG_DBG("Input buffer ready!\n"); + } + + if (dequeue_buf) { + int rc = 0; + struct v4l2_plane *planes = NULL; + struct mem_region *curr = NULL, *mregion = NULL; + struct list_head *reg_bufs = NULL; + struct index_bitmap *bitmap = NULL; + + planes = kzalloc(sizeof(*planes) * num_planes, + GFP_KERNEL); + buffer.m.planes = planes; + buffer.length = 1; + buffer.memory = V4L2_MEMORY_USERPTR; + rc = msm_vidc_dqbuf(inst->vidc_context, &buffer); + + if (rc) { + WFD_MSG_ERR("Error dequeuing buffer" \ + "from vidc: %d", rc); + goto abort_dequeue; + } + + reg_bufs = buffer.type == BUF_TYPE_OUTPUT ? + &inst->registered_output_bufs.list : + &inst->registered_input_bufs.list; + + bitmap = buffer.type == BUF_TYPE_OUTPUT ? + &inst->free_output_indices : + &inst->free_input_indices; + + list_for_each_entry(curr, reg_bufs, list) { + if ((u32)curr->paddr == + buffer.m.planes[0].m.userptr) { + mregion = curr; + break; + } + } + + if (!mregion) { + WFD_MSG_ERR("Got done msg for unknown buf\n"); + goto abort_dequeue; + } + + if (buffer.type == BUF_TYPE_OUTPUT && + inst->vmops.op_buffer_done) { + struct vb2_buffer *vb = + (struct vb2_buffer *)mregion->cookie; + + vb->v4l2_buf.flags = buffer.flags; + vb->v4l2_buf.timestamp = buffer.timestamp; + vb->v4l2_planes[0].bytesused = + buffer.m.planes[0].bytesused; + + inst->vmops.op_buffer_done( + inst->vmops.cbdata, 0, vb); + } else if (buffer.type == BUF_TYPE_INPUT && + inst->vmops.ip_buffer_done) { + inst->vmops.ip_buffer_done( + inst->vmops.cbdata, + 0, mregion); + } + + complete_all(&inst->dq_complete); + mutex_lock(&inst->lock); + mark_index_free(bitmap, buffer.index); + mutex_unlock(&inst->lock); +abort_dequeue: + kfree(planes); + } + } + + + WFD_MSG_DBG("Exiting callback thread\n"); + mutex_lock(&inst->lock); + inst->callback_thread_running = false; + mutex_unlock(&inst->lock); + return 0; +} + +static long venc_open(struct v4l2_subdev *sd, void *arg) +{ + struct venc_inst *inst = NULL; + struct venc_msg_ops *vmops = arg; + struct v4l2_event_subscription event = {0}; + int rc = 0; + + if (!vmops) { + WFD_MSG_ERR("Callbacks required for %s\n", __func__); + rc = -EINVAL; + goto venc_open_fail; + } else if (!sd) { + WFD_MSG_ERR("Subdevice required for %s\n", __func__); + rc = -EINVAL; + goto venc_open_fail; + } + + inst = kzalloc(sizeof(*inst), GFP_KERNEL); + if (!inst) { + WFD_MSG_ERR("Failed to allocate memory\n"); + rc = -EINVAL; + goto venc_open_fail; + } + + inst->vmops = *vmops; + INIT_LIST_HEAD(&inst->registered_output_bufs.list); + INIT_LIST_HEAD(&inst->registered_input_bufs.list); + init_completion(&inst->dq_complete); + init_completion(&inst->cmd_complete); + mutex_init(&inst->lock); + inst->vidc_context = msm_vidc_open(MSM_VIDC_CORE_0, MSM_VIDC_ENCODER); + if (!inst->vidc_context) { + WFD_MSG_ERR("Failed to create vidc context\n"); + rc = -ENXIO; + goto vidc_open_fail; + } + + event.type = V4L2_EVENT_MSM_VIDC_CLOSE_DONE; + rc = msm_vidc_subscribe_event(inst->vidc_context, &event); + if (rc) { + WFD_MSG_ERR("Failed to subscribe to CLOSE_DONE event\n"); + goto vidc_subscribe_fail; + } + + event.type = V4L2_EVENT_MSM_VIDC_FLUSH_DONE; + rc = msm_vidc_subscribe_event(inst->vidc_context, &event); + if (rc) { + WFD_MSG_ERR("Failed to subscribe to FLUSH_DONE event\n"); + goto vidc_subscribe_fail; + } + + inst->callback_thread = kthread_run(venc_vidc_callback_thread, inst, + "venc_vidc_callback_thread"); + if (IS_ERR(inst->callback_thread)) { + WFD_MSG_ERR("Failed to create callback thread\n"); + rc = PTR_ERR(inst->callback_thread); + inst->callback_thread = NULL; + goto vidc_kthread_create_fail; + } + inst->callback_thread_running = true; + + sd->dev_priv = inst; + vmops->cookie = inst; + return 0; +vidc_kthread_create_fail: + event.type = V4L2_EVENT_MSM_VIDC_CLOSE_DONE; + msm_vidc_unsubscribe_event(inst->vidc_context, &event); + + event.type = V4L2_EVENT_MSM_VIDC_FLUSH_DONE; + msm_vidc_unsubscribe_event(inst->vidc_context, &event); +vidc_subscribe_fail: + msm_vidc_close(inst->vidc_context); +vidc_open_fail: + kfree(inst); +venc_open_fail: + return rc; +} + +static long venc_close(struct v4l2_subdev *sd, void *arg) +{ + struct venc_inst *inst = NULL; + struct v4l2_event_subscription event = {0}; + struct v4l2_encoder_cmd enc_cmd = {0}; + int rc = 0; + + if (!sd) { + WFD_MSG_ERR("Subdevice required for %s\n", __func__); + rc = -EINVAL; + goto venc_close_fail; + } + + inst = (struct venc_inst *)sd->dev_priv; + enc_cmd.cmd = V4L2_ENC_CMD_STOP; + msm_vidc_encoder_cmd(inst->vidc_context, &enc_cmd); + + wait_for_completion(&inst->cmd_complete); + + if (inst->callback_thread && inst->callback_thread_running) + kthread_stop(inst->callback_thread); + + event.type = V4L2_EVENT_MSM_VIDC_CLOSE_DONE; + rc = msm_vidc_unsubscribe_event(inst->vidc_context, &event); + if (rc) + WFD_MSG_WARN("Failed to unsubscribe close event\n"); + + event.type = V4L2_EVENT_MSM_VIDC_FLUSH_DONE; + rc = msm_vidc_unsubscribe_event(inst->vidc_context, &event); + if (rc) + WFD_MSG_WARN("Failed to unsubscribe flush event\n"); + + rc = msm_vidc_close(inst->vidc_context); + if (rc) + WFD_MSG_WARN("Failed to close vidc context\n"); + + kfree(inst); + sd->dev_priv = inst = NULL; +venc_close_fail: + return rc; +} + +static long venc_get_buffer_req(struct v4l2_subdev *sd, void *arg) +{ + int rc = 0; + struct venc_inst *inst = NULL; + struct bufreq *bufreq = arg; + struct v4l2_requestbuffers v4l2_bufreq = {0}; + struct v4l2_format v4l2_format = {0}; + + if (!sd) { + WFD_MSG_ERR("Subdevice required for %s\n", __func__); + rc = -EINVAL; + goto venc_buf_req_fail; + } else if (!arg) { + WFD_MSG_ERR("Invalid buffer requirements\n"); + rc = -EINVAL; + goto venc_buf_req_fail; + } + + inst = (struct venc_inst *)sd->dev_priv; + /* Get buffer count */ + v4l2_bufreq = (struct v4l2_requestbuffers) { + .count = bufreq->count, + .type = BUF_TYPE_OUTPUT, + .memory = V4L2_MEMORY_USERPTR, + }; + + rc = msm_vidc_reqbufs(inst->vidc_context, &v4l2_bufreq); + if (rc) { + WFD_MSG_ERR("Failed getting buffer requirements\n"); + goto venc_buf_req_fail; + } + + /* Get buffer size */ + v4l2_format.type = BUF_TYPE_OUTPUT; + rc = msm_vidc_g_fmt(inst->vidc_context, &v4l2_format); + if (rc) { + WFD_MSG_ERR("Failed getting OP buffer size\n"); + goto venc_buf_req_fail; + } + + bufreq->count = v4l2_bufreq.count; + bufreq->size = v4l2_format.fmt.pix_mp.plane_fmt[0].sizeimage; + + inst->free_output_indices.size_bits = bufreq->count; + inst->free_output_indices.size = roundup(bufreq->count, + sizeof(unsigned long)) / sizeof(unsigned long); + inst->free_output_indices.bitmap = kzalloc(inst->free_output_indices. + size, GFP_KERNEL); +venc_buf_req_fail: + return rc; +} + +static long venc_set_buffer_req(struct v4l2_subdev *sd, void *arg) +{ + int rc = 0; + struct venc_inst *inst = NULL; + struct bufreq *bufreq = arg; + struct v4l2_requestbuffers v4l2_bufreq = {0}; + struct v4l2_format v4l2_format = {0}; + + if (!sd) { + WFD_MSG_ERR("Subdevice required for %s\n", __func__); + rc = -EINVAL; + goto venc_buf_req_fail; + } else if (!arg) { + WFD_MSG_ERR("Invalid buffer requirements\n"); + rc = -EINVAL; + goto venc_buf_req_fail; + } + + inst = (struct venc_inst *)sd->dev_priv; + + /* Attempt to set buffer count */ + v4l2_bufreq = (struct v4l2_requestbuffers) { + .count = bufreq->count, + .type = BUF_TYPE_INPUT, + .memory = V4L2_MEMORY_USERPTR, + }; + + rc = msm_vidc_reqbufs(inst->vidc_context, &v4l2_bufreq); + if (rc) { + WFD_MSG_ERR("Failed getting buffer requirements"); + goto venc_buf_req_fail; + } + + /* Get buffer size */ + v4l2_format.type = BUF_TYPE_INPUT; + rc = msm_vidc_g_fmt(inst->vidc_context, &v4l2_format); + if (rc) { + WFD_MSG_ERR("Failed getting OP buffer size\n"); + goto venc_buf_req_fail; + } + + bufreq->count = v4l2_bufreq.count; + bufreq->size = v4l2_format.fmt.pix_mp.plane_fmt[0].sizeimage; + + inst->free_input_indices.size_bits = bufreq->count; + inst->free_input_indices.size = roundup(bufreq->count, + sizeof(unsigned long)) / sizeof(unsigned long); + inst->free_input_indices.bitmap = kzalloc(inst->free_input_indices. + size, GFP_KERNEL); +venc_buf_req_fail: + return rc; +} + +static long venc_start(struct v4l2_subdev *sd) +{ + struct venc_inst *inst = NULL; + int rc = 0; + + if (!sd) { + WFD_MSG_ERR("Subdevice required for %s\n", __func__); + rc = -EINVAL; + goto venc_start_fail; + } + + inst = (struct venc_inst *)sd->dev_priv; + + rc = msm_vidc_streamon(inst->vidc_context, BUF_TYPE_OUTPUT); + if (rc) { + WFD_MSG_ERR("Failed to streamon vidc's output port"); + goto venc_start_fail; + } + + rc = msm_vidc_streamon(inst->vidc_context, BUF_TYPE_INPUT); + if (rc) { + WFD_MSG_ERR("Failed to streamon vidc's input port"); + goto venc_start_fail; + } + +venc_start_fail: + return rc; +} + +static long venc_stop(struct v4l2_subdev *sd) +{ + struct venc_inst *inst = NULL; + int rc = 0; + + if (!sd) { + WFD_MSG_ERR("Subdevice required for %s\n", __func__); + rc = -EINVAL; + goto venc_stop_fail; + } + + inst = (struct venc_inst *)sd->dev_priv; + + rc = msm_vidc_streamoff(inst->vidc_context, BUF_TYPE_INPUT); + if (rc) { + WFD_MSG_ERR("Failed to streamoff vidc's input port"); + goto venc_stop_fail; + } + + rc = msm_vidc_streamoff(inst->vidc_context, BUF_TYPE_OUTPUT); + if (rc) { + WFD_MSG_ERR("Failed to streamoff vidc's output port"); + goto venc_stop_fail; + } + +venc_stop_fail: + return rc; +} + +static long venc_set_input_buffer(struct v4l2_subdev *sd, void *arg) +{ + int rc = 0; + struct venc_inst *inst = NULL; + struct v4l2_buffer buf = {0}; + struct v4l2_plane plane = {0}; + struct mem_region *mregion = arg; + + if (!sd) { + WFD_MSG_ERR("Subdevice required for %s\n", __func__); + rc = -EINVAL; + goto set_input_buffer_fail; + } else if (!arg) { + WFD_MSG_ERR("Invalid input buffer\n"); + rc = -EINVAL; + goto set_input_buffer_fail; + } + + inst = (struct venc_inst *)sd->dev_priv; + if (get_registered_mregion(&inst->registered_input_bufs, mregion)) { + WFD_MSG_ERR("Duplicate input buffer\n"); + rc = -EEXIST; + goto set_input_buffer_fail; + } + + mregion = kzalloc(sizeof(*mregion), GFP_KERNEL); + *mregion = *(struct mem_region *)arg; + + plane = (struct v4l2_plane) { + .length = mregion->size, + .m.userptr = (u32)mregion->paddr, + }; + + buf = (struct v4l2_buffer) { + .index = get_list_len(&inst->registered_input_bufs), + .type = BUF_TYPE_INPUT, + .bytesused = 0, + .memory = V4L2_MEMORY_USERPTR, + .m.planes = &plane, + .length = 1, + }; + + WFD_MSG_DBG("Prepare %p with index, %d", + (void *)buf.m.planes[0].m.userptr, buf.index); + rc = msm_vidc_prepare_buf(inst->vidc_context, &buf); + if (rc) { + WFD_MSG_ERR("Failed to prepare input buffer\n"); + goto set_input_buffer_fail; + } + + list_add_tail(&mregion->list, &inst->registered_input_bufs.list); + return 0; +set_input_buffer_fail: + kfree(mregion); + return rc; +} + +static int venc_map_user_to_kernel(struct mem_region *mregion) +{ + int rc = 0; + unsigned long flags = 0, size = 0; + if (!mregion) { + rc = -EINVAL; + goto venc_map_fail; + } + + + mregion->ion_handle = ion_import_dma_buf(venc_ion_client, mregion->fd); + if (IS_ERR_OR_NULL(mregion->ion_handle)) { + rc = PTR_ERR(mregion->ion_handle); + WFD_MSG_ERR("Failed to get handle: %p, %d, %d, %d\n", + venc_ion_client, mregion->fd, mregion->offset, rc); + mregion->ion_handle = NULL; + goto venc_map_fail; + } + + rc = ion_handle_get_flags(venc_ion_client, mregion->ion_handle, &flags); + if (rc) { + WFD_MSG_ERR("Failed to get ion flags %d\n", rc); + goto venc_map_fail; + } + /* TODO: skip for secure */ + mregion->kvaddr = ion_map_kernel(venc_ion_client, + mregion->ion_handle, flags); + + if (IS_ERR_OR_NULL(mregion->kvaddr)) { + WFD_MSG_ERR("Failed to map buffer into kernel\n"); + rc = PTR_ERR(mregion->kvaddr); + mregion->kvaddr = NULL; + goto venc_map_fail; + } + + rc = ion_map_iommu(venc_ion_client, mregion->ion_handle, + VIDEO_DOMAIN, VIDEO_MAIN_POOL, SZ_4K, + 0, (unsigned long *)&mregion->paddr, &size, flags, 0); + + if (rc) { + WFD_MSG_ERR("Failed to map into iommu\n"); + goto venc_map_iommu_map_fail; + } else if (size < mregion->size) { + WFD_MSG_ERR("Failed to iommu map the correct size\n"); + goto venc_map_iommu_size_fail; + } + + return 0; +venc_map_iommu_size_fail: + ion_unmap_iommu(venc_ion_client, mregion->ion_handle, + VIDEO_DOMAIN, VIDEO_MAIN_POOL); +venc_map_iommu_map_fail: + ion_unmap_kernel(venc_ion_client, mregion->ion_handle); +venc_map_fail: + return rc; +} + +static int venc_unmap_user_to_kernel(struct mem_region *mregion) +{ + if (!mregion || !mregion->ion_handle) + return 0; + + if (mregion->paddr) { + ion_unmap_iommu(venc_ion_client, mregion->ion_handle, + VIDEO_DOMAIN, VIDEO_MAIN_POOL); + mregion->paddr = NULL; + } + + if (mregion->kvaddr) { + ion_unmap_kernel(venc_ion_client, mregion->ion_handle); + mregion->kvaddr = NULL; + } + + + return 0; +} + +static long venc_set_output_buffer(struct v4l2_subdev *sd, void *arg) +{ + int rc = 0; + struct venc_inst *inst = NULL; + struct v4l2_buffer buf = {0}; + struct v4l2_plane plane = {0}; + struct mem_region *mregion = arg; + + if (!sd) { + WFD_MSG_ERR("Subdevice required for %s\n", __func__); + rc = -EINVAL; + goto venc_set_output_buffer_fail; + } else if (!mregion) { + WFD_MSG_ERR("Invalid output buffer\n"); + rc = -EINVAL; + goto venc_set_output_buffer_fail; + } + + inst = (struct venc_inst *)sd->dev_priv; + + /* Check if buf already registered */ + if (get_registered_mregion(&inst->registered_output_bufs, mregion)) { + WFD_MSG_ERR("Duplicate output buffer\n"); + rc = -EEXIST; + goto venc_set_output_buffer_fail; + } + + mregion = kzalloc(sizeof(*mregion), GFP_KERNEL); + + if (!mregion) { + WFD_MSG_ERR("Failed to allocate memory\n"); + goto venc_set_output_buffer_fail; + } + + *mregion = *(struct mem_region *)arg; + INIT_LIST_HEAD(&mregion->list); + + rc = venc_map_user_to_kernel(mregion); + if (rc) { + WFD_MSG_ERR("Failed to map output buffer\n"); + goto venc_set_output_buffer_map_fail; + } + + plane = (struct v4l2_plane) { + .length = mregion->size, + .m.userptr = (u32)mregion->paddr, + }; + + buf = (struct v4l2_buffer) { + .index = get_list_len(&inst->registered_output_bufs), + .type = BUF_TYPE_OUTPUT, + .bytesused = 0, + .memory = V4L2_MEMORY_USERPTR, + .m.planes = &plane, + .length = 1, + }; + + WFD_MSG_DBG("Prepare %p with index, %d", + (void *)buf.m.planes[0].m.userptr, buf.index); + rc = msm_vidc_prepare_buf(inst->vidc_context, &buf); + if (rc) { + WFD_MSG_ERR("Failed to prepare output buffer\n"); + goto venc_set_output_buffer_prepare_fail; + } + + list_add_tail(&mregion->list, &inst->registered_output_bufs.list); + return rc; +venc_set_output_buffer_prepare_fail: + venc_unmap_user_to_kernel(mregion); +venc_set_output_buffer_map_fail: + kfree(mregion); +venc_set_output_buffer_fail: + return rc; +} + +static long venc_set_format(struct v4l2_subdev *sd, void *arg) +{ + struct venc_inst *inst = NULL; + struct v4l2_format *fmt = arg, temp; + int rc = 0; + + if (!sd) { + WFD_MSG_ERR("Subdevice required for %s\n", __func__); + rc = -EINVAL; + goto venc_set_format_fail; + } else if (!fmt) { + WFD_MSG_ERR("Invalid format\n"); + rc = -EINVAL; + goto venc_set_format_fail; + } else if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + WFD_MSG_ERR("Invalid buffer type %d\n", fmt->type); + rc = -ENOTSUPP; + goto venc_set_format_fail; + } + + inst = (struct venc_inst *)sd->dev_priv; + temp = (struct v4l2_format) { + .type = BUF_TYPE_OUTPUT, + .fmt.pix_mp = (struct v4l2_pix_format_mplane) { + .width = fmt->fmt.pix.width, + .height = fmt->fmt.pix.height, + .pixelformat = fmt->fmt.pix.pixelformat, + }, + }; + + rc = msm_vidc_s_fmt(inst->vidc_context, &temp); + + if (rc) { + WFD_MSG_ERR("Failed to format for output port\n"); + goto venc_set_format_fail; + } else if (!temp.fmt.pix_mp.num_planes) { + WFD_MSG_ERR("No. of planes for output buffers make no sense\n"); + rc = -EINVAL; + goto venc_set_format_fail; + } + fmt->fmt.pix.sizeimage = temp.fmt.pix_mp.plane_fmt[0].sizeimage; + inst->num_output_planes = temp.fmt.pix_mp.num_planes; + + temp.type = BUF_TYPE_INPUT; + temp.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12; + rc = msm_vidc_s_fmt(inst->vidc_context, &temp); + inst->num_input_planes = temp.fmt.pix_mp.num_planes; + + if (rc) { + WFD_MSG_ERR("Failed to format for input port\n"); + goto venc_set_format_fail; + } +venc_set_format_fail: + return rc; +} + +static long venc_set_framerate(struct v4l2_subdev *sd, void *arg) +{ + struct venc_inst *inst = NULL; + struct v4l2_control ctrl = {0}; + + if (!sd) { + WFD_MSG_ERR("Subdevice required for %s\n", __func__); + return -EINVAL; + } else if (!arg) { + WFD_MSG_ERR("Invalid framerate\n"); + return -EINVAL; + } + + inst = (struct venc_inst *)sd->dev_priv; + ctrl.id = V4L2_CID_MPEG_VIDC_VIDEO_FRAME_RATE; + ctrl.value = 30; + return msm_vidc_s_ctrl(inst->vidc_context, &ctrl); +} + +static long venc_fill_outbuf(struct v4l2_subdev *sd, void *arg) +{ + struct venc_inst *inst = NULL; + struct mem_region *mregion = NULL; + struct v4l2_buffer buffer = {0}; + struct v4l2_plane plane = {0}; + int index = 0, rc = 0; + + if (!sd) { + WFD_MSG_ERR("Subdevice required for %s\n", __func__); + return -EINVAL; + } else if (!arg) { + WFD_MSG_ERR("Invalid output buffer ot fill\n"); + return -EINVAL; + } + + inst = (struct venc_inst *)sd->dev_priv; + mregion = get_registered_mregion(&inst->registered_output_bufs, arg); + + if (!mregion) { + WFD_MSG_ERR("Output buffer not registered\n"); + return -ENOENT; + } + + plane = (struct v4l2_plane) { + .length = mregion->size, + .m.userptr = (u32)mregion->paddr, + }; + + while (true) { + mutex_lock(&inst->lock); + index = next_free_index(&inst->free_output_indices); + mutex_unlock(&inst->lock); + + if (index < 0) + wait_for_completion(&inst->dq_complete); + else + break; + } + + buffer = (struct v4l2_buffer) { + .index = index, + .type = BUF_TYPE_OUTPUT, + .memory = V4L2_MEMORY_USERPTR, + .m.planes = &plane, + .length = 1, + }; + + WFD_MSG_DBG("Fill buffer %p with index, %d", + (void *)buffer.m.planes[0].m.userptr, buffer.index); + rc = msm_vidc_qbuf(inst->vidc_context, &buffer); + if (!rc) { + mutex_lock(&inst->lock); + mark_index_busy(&inst->free_output_indices, index); + mutex_unlock(&inst->lock); + } + return rc; + +} + +static long venc_encode_frame(struct v4l2_subdev *sd, void *arg) +{ + struct venc_inst *inst = NULL; + struct venc_buf_info *venc_buf = arg; + struct mem_region *mregion = NULL; + struct v4l2_buffer buffer = {0}; + struct v4l2_plane plane = {0}; + int index = 0, rc = 0; + + if (!sd) { + WFD_MSG_ERR("Subdevice required for %s\n", __func__); + return -EINVAL; + } else if (!venc_buf) { + WFD_MSG_ERR("Invalid output buffer ot fill\n"); + return -EINVAL; + } + + inst = (struct venc_inst *)sd->dev_priv; + mregion = venc_buf->mregion; + + plane = (struct v4l2_plane) { + .length = mregion->size, + .m.userptr = (u32)mregion->paddr, + .bytesused = mregion->size, + }; + + while (true) { + mutex_lock(&inst->lock); + index = next_free_index(&inst->free_input_indices); + mutex_unlock(&inst->lock); + + if (index < 0) + wait_for_completion(&inst->dq_complete); + else + break; + } + + buffer = (struct v4l2_buffer) { + .index = index, + .type = BUF_TYPE_INPUT, + .timestamp = ns_to_timeval(venc_buf->timestamp), + .memory = V4L2_MEMORY_USERPTR, + .m.planes = &plane, + .length = 1, + }; + + WFD_MSG_DBG("Encode buffer %p with index, %d", + (void *)buffer.m.planes[0].m.userptr, buffer.index); + rc = msm_vidc_qbuf(inst->vidc_context, &buffer); + if (!rc) { + mutex_lock(&inst->lock); + mark_index_busy(&inst->free_input_indices, index); + mutex_unlock(&inst->lock); + } + return rc; +} + +static long venc_alloc_recon_buffers(struct v4l2_subdev *sd, void *arg) +{ + /* vidc driver allocates internally on streamon */ + return 0; +} + +static long venc_free_buffer(struct venc_inst *inst, int type, + struct mem_region *to_free, bool unmap_user_buffer) +{ + struct mem_region *mregion = NULL; + struct mem_region *buf_list = NULL; + + if (type == BUF_TYPE_OUTPUT) { + buf_list = &inst->registered_output_bufs; + } else if (type == BUF_TYPE_INPUT) { + buf_list = &inst->registered_input_bufs; + } else { + WFD_MSG_ERR("Trying to free a buffer of unknown type\n"); + return -EINVAL; + } + + mregion = get_registered_mregion(buf_list, to_free); + + if (!mregion) { + WFD_MSG_ERR("Buffer not registered, cannot free\n"); + return -ENOENT; + } + + if (unmap_user_buffer) { + int rc = venc_unmap_user_to_kernel(mregion); + if (rc) + WFD_MSG_WARN("Unable to unmap user buffer\n"); + } + + list_del(&mregion->list); + kfree(mregion); + return 0; +} +static long venc_free_output_buffer(struct v4l2_subdev *sd, void *arg) +{ + int rc = 0; + struct venc_inst *inst = NULL; + + if (!sd) { + WFD_MSG_ERR("Subdevice required for %s\n", __func__); + rc = -EINVAL; + goto venc_free_output_buffer_fail; + } else if (!arg) { + WFD_MSG_ERR("Invalid output buffer\n"); + rc = -EINVAL; + goto venc_free_output_buffer_fail; + } + + inst = (struct venc_inst *)sd->dev_priv; + return venc_free_buffer(inst, BUF_TYPE_OUTPUT, arg, true); +venc_free_output_buffer_fail: + return rc; +} + +static long venc_flush_buffers(struct v4l2_subdev *sd, void *arg) +{ + struct venc_inst *inst = NULL; + struct v4l2_encoder_cmd enc_cmd = {0}; + int rc = 0; + + if (!sd) { + WFD_MSG_ERR("Subdevice required for %s\n", __func__); + rc = -EINVAL; + goto venc_flush_buffers_fail; + } + + inst = (struct venc_inst *)sd->dev_priv; + + enc_cmd.cmd = V4L2_ENC_QCOM_CMD_FLUSH; + enc_cmd.flags = BUF_TYPE_INPUT | BUF_TYPE_OUTPUT; + msm_vidc_encoder_cmd(inst->vidc_context, &enc_cmd); + + wait_for_completion(&inst->cmd_complete); +venc_flush_buffers_fail: + return rc; +} + +static long venc_free_input_buffer(struct v4l2_subdev *sd, void *arg) +{ + int rc = 0; + struct venc_inst *inst = NULL; + + if (!sd) { + WFD_MSG_ERR("Subdevice required for %s\n", __func__); + rc = -EINVAL; + goto venc_free_input_buffer_fail; + } else if (!arg) { + WFD_MSG_ERR("Invalid output buffer\n"); + rc = -EINVAL; + goto venc_free_input_buffer_fail; + } + + inst = (struct venc_inst *)sd->dev_priv; + return venc_free_buffer(inst, BUF_TYPE_INPUT, arg, false); +venc_free_input_buffer_fail: + return rc; +} + +static long venc_free_recon_buffers(struct v4l2_subdev *sd, void *arg) +{ + /* vidc driver takes care of this */ + return 0; +} + +static long venc_set_property(struct v4l2_subdev *sd, void *arg) +{ + struct venc_inst *inst = NULL; + struct v4l2_control *ctrl = arg; + + if (!sd) { + WFD_MSG_ERR("Subdevice required for %s\n", __func__); + return -EINVAL; + } + + inst = (struct venc_inst *)sd->dev_priv; + if (ctrl->id == V4L2_CID_MPEG_VIDEO_HEADER_MODE) { + /* XXX: We don't support this yet, but to prevent unncessary + * target specific code for the client, we'll not error out. + * The client ideally shouldn't notice this */ + return 0; + } + + return msm_vidc_s_ctrl(inst->vidc_context, (struct v4l2_control *)arg); +} + +static long venc_get_property(struct v4l2_subdev *sd, void *arg) +{ + struct venc_inst *inst = NULL; + + if (!sd) { + WFD_MSG_ERR("Subdevice required for %s\n", __func__); + return -EINVAL; + } + + inst = (struct venc_inst *)sd->dev_priv; + return msm_vidc_g_ctrl(inst->vidc_context, (struct v4l2_control *)arg); +} + +long venc_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + long rc = 0; + switch (cmd) { + case OPEN: + rc = venc_open(sd, arg); + break; + case CLOSE: + rc = venc_close(sd, arg); + break; + case ENCODE_START: + rc = venc_start(sd); + break; + case ENCODE_FRAME: + venc_encode_frame(sd, arg); + break; + case ENCODE_STOP: + rc = venc_stop(sd); + break; + case SET_PROP: + rc = venc_set_property(sd, arg); + break; + case GET_PROP: + rc = venc_get_property(sd, arg); + break; + case GET_BUFFER_REQ: + rc = venc_get_buffer_req(sd, arg); + break; + case SET_BUFFER_REQ: + rc = venc_set_buffer_req(sd, arg); + break; + case FREE_BUFFER: + break; + case FILL_OUTPUT_BUFFER: + rc = venc_fill_outbuf(sd, arg); + break; + case SET_FORMAT: + rc = venc_set_format(sd, arg); + break; + case SET_FRAMERATE: + rc = venc_set_framerate(sd, arg); + break; + case SET_INPUT_BUFFER: + rc = venc_set_input_buffer(sd, arg); + break; + case SET_OUTPUT_BUFFER: + rc = venc_set_output_buffer(sd, arg); + break; + case ALLOC_RECON_BUFFERS: + rc = venc_alloc_recon_buffers(sd, arg); + break; + case FREE_OUTPUT_BUFFER: + rc = venc_free_output_buffer(sd, arg); + break; + case FREE_INPUT_BUFFER: + rc = venc_free_input_buffer(sd, arg); + break; + case FREE_RECON_BUFFERS: + rc = venc_free_recon_buffers(sd, arg); + break; + case ENCODE_FLUSH: + rc = venc_flush_buffers(sd, arg); + break; + default: + WFD_MSG_ERR("Unknown ioctl %d to enc-subdev\n", cmd); + rc = -ENOTSUPP; + break; + } + return rc; +} diff --git a/drivers/media/video/msm_wfd/mdp-subdev.c b/drivers/media/video/msm_wfd/mdp-4-subdev.c similarity index 90% rename from drivers/media/video/msm_wfd/mdp-subdev.c rename to drivers/media/video/msm_wfd/mdp-4-subdev.c index 886b0baff1f8..7db0a3677d24 100644 --- a/drivers/media/video/msm_wfd/mdp-subdev.c +++ b/drivers/media/video/msm_wfd/mdp-4-subdev.c @@ -19,6 +19,8 @@ struct mdp_instance { struct fb_info *mdp; u32 height; u32 width; + bool secure; + bool uses_iommu_split_domain; }; int mdp_init(struct v4l2_subdev *sd, u32 val) @@ -29,34 +31,42 @@ int mdp_open(struct v4l2_subdev *sd, void *arg) { struct mdp_instance *inst = kzalloc(sizeof(struct mdp_instance), GFP_KERNEL); - void **cookie = (void **)arg; + struct mdp_msg_ops *mops = arg; int rc = 0; struct fb_info *fbi = NULL; if (!inst) { WFD_MSG_ERR("Out of memory\n"); - return -ENOMEM; + rc = -ENOMEM; + goto mdp_open_fail; + } else if (!mops) { + WFD_MSG_ERR("Invalid arguments\n"); + rc = -EINVAL; + goto mdp_open_fail; } fbi = msm_fb_get_writeback_fb(); if (!fbi) { WFD_MSG_ERR("Failed to acquire mdp instance\n"); rc = -ENODEV; - goto exit; + goto mdp_open_fail; } /*Tell HDMI daemon to open fb2*/ rc = kobject_uevent(&fbi->dev->kobj, KOBJ_ADD); if (rc) { WFD_MSG_ERR("Failed add to kobj"); - goto exit; + goto mdp_open_fail; } msm_fb_writeback_init(fbi); inst->mdp = fbi; - *cookie = inst; + inst->secure = mops->secure; + inst->uses_iommu_split_domain = mops->iommu_split_domain; + + mops->cookie = inst; return rc; -exit: +mdp_open_fail: kfree(inst); return rc; } @@ -134,8 +144,8 @@ int mdp_q_buffer(struct v4l2_subdev *sd, void *arg) fbdata.flags = 0; fbdata.priv = (uint32_t)binfo->cookie; - WFD_MSG_INFO("queue buffer to mdp with offset = %u," - "fd = %u, priv = %p, iova = %p\n", + WFD_MSG_INFO("queue buffer to mdp with offset = %u, fd = %u, "\ + "priv = %p, iova = %p\n", fbdata.offset, fbdata.memory_id, (void *)fbdata.priv, (void *)fbdata.iova); rc = msm_fb_writeback_queue_buffer(inst->mdp, &fbdata); @@ -179,6 +189,7 @@ int mdp_set_prop(struct v4l2_subdev *sd, void *arg) inst->width = prop->width; return 0; } + long mdp_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) { int rc = 0; diff --git a/drivers/media/video/msm_wfd/mdp-5-subdev.c b/drivers/media/video/msm_wfd/mdp-5-subdev.c new file mode 100644 index 000000000000..0e57d4a577cc --- /dev/null +++ b/drivers/media/video/msm_wfd/mdp-5-subdev.c @@ -0,0 +1,229 @@ +/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 and +* only version 2 as published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +*/ +#include +#include +#include +#include "enc-subdev.h" +#include "mdp-subdev.h" +#include "wfd-util.h" + + +struct mdp_instance { + struct fb_info *mdp; + u32 height; + u32 width; + bool secure; +}; + +int mdp_init(struct v4l2_subdev *sd, u32 val) +{ + return 0; +} + +int mdp_open(struct v4l2_subdev *sd, void *arg) +{ + struct mdp_instance *inst = kzalloc(sizeof(struct mdp_instance), + GFP_KERNEL); + struct mdp_msg_ops *mops = arg; + int rc = 0; + struct fb_info *fbi = NULL; + + if (!inst) { + WFD_MSG_ERR("Out of memory\n"); + rc = -ENOMEM; + goto mdp_open_fail; + } else if (!mops) { + WFD_MSG_ERR("Invalid arguments\n"); + rc = -EINVAL; + goto mdp_open_fail; + } + + fbi = msm_fb_get_writeback_fb(); + if (!fbi) { + WFD_MSG_ERR("Failed to acquire mdp instance\n"); + rc = -ENODEV; + goto mdp_open_fail; + } + + /*Tell HDMI daemon to open fb2*/ + rc = kobject_uevent(&fbi->dev->kobj, KOBJ_ADD); + if (rc) + WFD_MSG_ERR("Failed add to kobj"); + + msm_fb_writeback_init(fbi); + inst->mdp = fbi; + inst->secure = mops->secure; + + mops->cookie = inst; + return rc; +mdp_open_fail: + kfree(inst); + return rc; +} + +int mdp_start(struct v4l2_subdev *sd, void *arg) +{ + struct mdp_instance *inst = arg; + int rc = 0; + struct fb_info *fbi = NULL; + if (inst) { + rc = msm_fb_writeback_start(inst->mdp); + if (rc) { + WFD_MSG_ERR("Failed to start MDP mode\n"); + goto exit; + } + fbi = msm_fb_get_writeback_fb(); + if (!fbi) { + WFD_MSG_ERR("Failed to acquire mdp instance\n"); + rc = -ENODEV; + goto exit; + } + rc = kobject_uevent(&fbi->dev->kobj, KOBJ_ONLINE); + if (rc) + WFD_MSG_ERR("Failed to send ONLINE event\n"); + } +exit: + return rc; +} + +int mdp_stop(struct v4l2_subdev *sd, void *arg) +{ + struct mdp_instance *inst = arg; + int rc = 0; + struct fb_info *fbi = NULL; + if (inst) { + rc = msm_fb_writeback_stop(inst->mdp); + if (rc) { + WFD_MSG_ERR("Failed to stop writeback mode\n"); + return rc; + } + fbi = (struct fb_info *)inst->mdp; + rc = kobject_uevent(&fbi->dev->kobj, KOBJ_OFFLINE); + if (rc) { + WFD_MSG_ERR("Failed to send offline event\n"); + return -EIO; + } + } + return 0; +} +int mdp_close(struct v4l2_subdev *sd, void *arg) +{ + struct mdp_instance *inst = arg; + struct fb_info *fbi = NULL; + if (inst) { + fbi = (struct fb_info *)inst->mdp; + msm_fb_writeback_terminate(fbi); + kfree(inst); + } + return 0; +} +int mdp_q_buffer(struct v4l2_subdev *sd, void *arg) +{ + int rc = 0; + struct mdp_buf_info *binfo = arg; + struct msmfb_data fbdata; + struct mdp_instance *inst; + if (!binfo || !binfo->inst || !binfo->cookie) { + WFD_MSG_ERR("Invalid argument\n"); + return -EINVAL; + } + inst = binfo->inst; + fbdata.offset = binfo->offset; + fbdata.memory_id = binfo->fd; + fbdata.iova = binfo->paddr; + fbdata.id = 0; + fbdata.flags = 0; + fbdata.priv = (uint32_t)binfo->cookie; + + WFD_MSG_DBG("queue buffer to mdp with offset = %u, fd = %u, "\ + "priv = %p, iova = %p\n", + fbdata.offset, fbdata.memory_id, + (void *)fbdata.priv, (void *)fbdata.iova); + rc = msm_fb_writeback_queue_buffer(inst->mdp, &fbdata); + + if (rc) + WFD_MSG_ERR("Failed to queue buffer\n"); + return rc; +} +int mdp_dq_buffer(struct v4l2_subdev *sd, void *arg) +{ + int rc = 0; + struct mdp_buf_info *obuf = arg; + struct msmfb_data fbdata; + struct mdp_instance *inst; + if (!arg) { + WFD_MSG_ERR("Invalid argument\n"); + return -EINVAL; + } + + inst = obuf->inst; + fbdata.flags = MSMFB_WRITEBACK_DEQUEUE_BLOCKING; + rc = msm_fb_writeback_dequeue_buffer(inst->mdp, &fbdata); + if (rc) { + WFD_MSG_ERR("Failed to dequeue buffer\n"); + return rc; + } + WFD_MSG_DBG("dequeue buf from mdp with priv = %u\n", + fbdata.priv); + obuf->cookie = (void *)fbdata.priv; + return rc; +} +int mdp_set_prop(struct v4l2_subdev *sd, void *arg) +{ + struct mdp_prop *prop = (struct mdp_prop *)arg; + struct mdp_instance *inst = prop->inst; + if (!prop || !inst) { + WFD_MSG_ERR("Invalid arguments\n"); + return -EINVAL; + } + inst->height = prop->height; + inst->width = prop->width; + return 0; +} + +long mdp_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + int rc = 0; + if (!sd) { + WFD_MSG_ERR("Invalid arguments\n"); + return -EINVAL; + } + switch (cmd) { + case MDP_Q_BUFFER: + rc = mdp_q_buffer(sd, arg); + break; + case MDP_DQ_BUFFER: + rc = mdp_dq_buffer(sd, arg); + break; + case MDP_OPEN: + rc = mdp_open(sd, arg); + break; + case MDP_START: + rc = mdp_start(sd, arg); + break; + case MDP_STOP: + rc = mdp_stop(sd, arg); + break; + case MDP_SET_PROP: + rc = mdp_set_prop(sd, arg); + break; + case MDP_CLOSE: + rc = mdp_close(sd, arg); + break; + default: + WFD_MSG_ERR("IOCTL: %u not supported\n", cmd); + rc = -EINVAL; + break; + } + return rc; +} diff --git a/drivers/media/video/msm_wfd/mdp-dummy-subdev.c b/drivers/media/video/msm_wfd/mdp-dummy-subdev.c new file mode 100644 index 000000000000..7ccbd7297073 --- /dev/null +++ b/drivers/media/video/msm_wfd/mdp-dummy-subdev.c @@ -0,0 +1,162 @@ +/* Copyright (c) 2012, The Linux Foundation. All rights reserved. +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 and +* only version 2 as published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +*/ +#include +#include +#include + +#include "mdp-subdev.h" +#include "wfd-util.h" + +struct mdp_buf_queue { + struct mdp_buf_info mdp_buf_info; + struct list_head node; +}; + +struct mdp_instance { + struct mdp_buf_queue mdp_bufs; + struct mutex mutex; +}; + +int mdp_init(struct v4l2_subdev *sd, u32 val) +{ + return 0; +} +int mdp_open(struct v4l2_subdev *sd, void *arg) +{ + struct mdp_instance *inst = kzalloc(sizeof(struct mdp_instance), + GFP_KERNEL); + void **cookie = (void **)arg; + int rc = 0; + + if (!inst) { + WFD_MSG_ERR("Out of memory\n"); + return -ENOMEM; + } + + INIT_LIST_HEAD(&inst->mdp_bufs.node); + mutex_init(&inst->mutex); + *cookie = inst; + return rc; +} + +int mdp_start(struct v4l2_subdev *sd, void *arg) +{ + return 0; +} +int mdp_stop(struct v4l2_subdev *sd, void *arg) +{ + return 0; +} +int mdp_close(struct v4l2_subdev *sd, void *arg) +{ + return 0; +} +int mdp_q_buffer(struct v4l2_subdev *sd, void *arg) +{ + static int foo; + int rc = 0; + struct mdp_buf_info *binfo = arg; + struct mdp_instance *inst = NULL; + + if (!binfo || !binfo->inst || !binfo->cookie) { + WFD_MSG_ERR("Invalid argument\n"); + return -EINVAL; + } + + + inst = binfo->inst; + if (binfo->kvaddr) { + struct mdp_buf_queue *new_entry = kzalloc(sizeof(*new_entry), + GFP_KERNEL); + memset((void *)binfo->kvaddr, foo++, 1024); + new_entry->mdp_buf_info = *binfo; + mutex_lock(&inst->mutex); + list_add_tail(&new_entry->node, &inst->mdp_bufs.node); + mutex_unlock(&inst->mutex); + WFD_MSG_DBG("Queue %p with cookie %p\n", + (void *)binfo->paddr, (void *)binfo->cookie); + } else { + rc = -EINVAL; + } + + return rc; +} +int mdp_dq_buffer(struct v4l2_subdev *sd, void *arg) +{ + struct mdp_buf_info *binfo = arg; + struct mdp_buf_queue *head = NULL; + struct mdp_instance *inst = NULL; + + inst = binfo->inst; + + while (head == NULL) { + mutex_lock(&inst->mutex); + if (!list_empty(&inst->mdp_bufs.node)) + head = list_first_entry(&inst->mdp_bufs.node, + struct mdp_buf_queue, node); + mutex_unlock(&inst->mutex); + } + + if (head == NULL) + return -ENOBUFS; + + mutex_lock(&inst->mutex); + list_del(&head->node); + mutex_unlock(&inst->mutex); + + *binfo = head->mdp_buf_info; + WFD_MSG_DBG("Dequeue %p with cookie %p\n", + (void *)binfo->paddr, (void *)binfo->cookie); + return 0; + +} +int mdp_set_prop(struct v4l2_subdev *sd, void *arg) +{ + return 0; +} +long mdp_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + int rc = 0; + if (!sd) { + WFD_MSG_ERR("Invalid arguments\n"); + return -EINVAL; + } + switch (cmd) { + case MDP_Q_BUFFER: + rc = mdp_q_buffer(sd, arg); + break; + case MDP_DQ_BUFFER: + rc = mdp_dq_buffer(sd, arg); + break; + case MDP_OPEN: + rc = mdp_open(sd, arg); + break; + case MDP_START: + rc = mdp_start(sd, arg); + break; + case MDP_STOP: + rc = mdp_stop(sd, arg); + break; + case MDP_SET_PROP: + rc = mdp_set_prop(sd, arg); + break; + case MDP_CLOSE: + rc = mdp_close(sd, arg); + break; + default: + WFD_MSG_ERR("IOCTL: %u not supported\n", cmd); + rc = -EINVAL; + break; + } + return rc; +} diff --git a/drivers/media/video/msm_wfd/mdp-subdev.h b/drivers/media/video/msm_wfd/mdp-subdev.h index 081fead129ef..4e8da3f0180f 100644 --- a/drivers/media/video/msm_wfd/mdp-subdev.h +++ b/drivers/media/video/msm_wfd/mdp-subdev.h @@ -34,6 +34,12 @@ struct mdp_prop { u32 width; }; +struct mdp_msg_ops { + void *cookie; + bool secure; + bool iommu_split_domain; +}; + static inline bool mdp_buf_info_equals(struct mdp_buf_info *a, struct mdp_buf_info *b) { diff --git a/drivers/media/video/msm_wfd/wfd-ioctl.c b/drivers/media/video/msm_wfd/wfd-ioctl.c index 83a9b6cad9d1..f1627531b4f6 100644 --- a/drivers/media/video/msm_wfd/wfd-ioctl.c +++ b/drivers/media/video/msm_wfd/wfd-ioctl.c @@ -10,7 +10,6 @@ * GNU General Public License for more details. * */ - #include #include #include @@ -40,8 +39,8 @@ #define WFD_NUM_DEVICES 2 #define WFD_DEVICE_NUMBER_BASE 38 #define WFD_DEVICE_SECURE (WFD_DEVICE_NUMBER_BASE + 1) -#define DEFAULT_WFD_WIDTH 640 -#define DEFAULT_WFD_HEIGHT 480 +#define DEFAULT_WFD_WIDTH 1280 +#define DEFAULT_WFD_HEIGHT 720 #define VENC_INPUT_BUFFERS 4 struct wfd_device { @@ -882,7 +881,7 @@ static int wfd_register_out_buf(struct wfd_inst *inst, list_add_tail(&minfo_entry->list, &inst->minfo_list); spin_unlock_irqrestore(&inst->inst_lock, flags); } else - WFD_MSG_INFO("Buffer already registered\n"); + WFD_MSG_DBG("Buffer already registered\n"); return 0; } @@ -968,7 +967,7 @@ static int wfdioc_dqbuf(struct file *filp, void *fh, struct wfd_inst *inst = filp->private_data; int rc; - WFD_MSG_INFO("Waiting to dequeue buffer\n"); + WFD_MSG_DBG("Waiting to dequeue buffer\n"); rc = vb2_dqbuf(&inst->vid_bufq, b, 0); if (rc) @@ -1304,6 +1303,7 @@ static int wfd_open(struct file *filp) struct wfd_inst *inst = NULL; struct wfd_device *wfd_dev = NULL; struct venc_msg_ops enc_mops; + struct mdp_msg_ops mdp_mops; struct vsg_msg_ops vsg_mops; WFD_MSG_DBG("wfd_open: E\n"); @@ -1337,12 +1337,15 @@ static int wfd_open(struct file *filp) wfd_stats_init(&inst->stats, MINOR(filp->f_dentry->d_inode->i_rdev)); + mdp_mops.secure = wfd_dev->secure_device; + mdp_mops.iommu_split_domain = wfd_dev->mdp_iommu_split_domain; rc = v4l2_subdev_call(&wfd_dev->mdp_sdev, core, ioctl, MDP_OPEN, - (void *)&inst->mdp_inst); + (void *)&mdp_mops); if (rc) { WFD_MSG_ERR("Failed to open mdp subdevice: %d\n", rc); goto err_mdp_open; } + inst->mdp_inst = mdp_mops.cookie; rc = v4l2_subdev_call(&wfd_dev->enc_sdev, core, load_fw); if (rc) { diff --git a/drivers/media/video/msm_wfd/wfd-util.h b/drivers/media/video/msm_wfd/wfd-util.h index b6bb245caff4..2fe73608b6d6 100644 --- a/drivers/media/video/msm_wfd/wfd-util.h +++ b/drivers/media/video/msm_wfd/wfd-util.h @@ -21,16 +21,11 @@ /*#define DEBUG_WFD*/ #define WFD_TAG "wfd: " -#ifdef DEBUG_WFD - #define WFD_MSG_INFO(fmt...) pr_info(WFD_TAG fmt) - #define WFD_MSG_WARN(fmt...) pr_warning(WFD_TAG fmt) -#else - #define WFD_MSG_INFO(fmt...) - #define WFD_MSG_WARN(fmt...) -#endif - #define WFD_MSG_ERR(fmt...) pr_err(KERN_ERR WFD_TAG fmt) - #define WFD_MSG_CRIT(fmt...) pr_crit(KERN_CRIT WFD_TAG fmt) - #define WFD_MSG_DBG(fmt...) pr_debug(WFD_TAG fmt) +#define WFD_MSG_INFO(fmt...) pr_info(WFD_TAG fmt) +#define WFD_MSG_WARN(fmt...) pr_warning(WFD_TAG fmt) +#define WFD_MSG_ERR(fmt...) pr_err(KERN_ERR WFD_TAG fmt) +#define WFD_MSG_CRIT(fmt...) pr_crit(KERN_CRIT WFD_TAG fmt) +#define WFD_MSG_DBG(fmt...) pr_debug(WFD_TAG fmt) struct wfd_stats_encode_sample {